Delete the legacy backend

This commit is contained in:
lax1dude
2025-05-17 14:05:33 -07:00
parent 7e772e2502
commit 71c61e33fd
328 changed files with 1 additions and 56280 deletions

View File

@ -11,7 +11,6 @@
- **Source code to provide the LWJGL keyboard, mouse, and OpenGL APIs in a browser** - **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** - **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** - **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: ### This repository does NOT contain:
@ -85,57 +84,7 @@ If you are creating a resource pack and want to disable the blur filter on the m
## Making a Server ## 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. To make a server for EaglercraftX 1.8 the recommended software to use is EaglercraftXServer ("EaglerXServer"), which you can get from lax1dude here: [https://lax1dude.net/eaglerxserver/](https://lax1dude.net/eaglerxserver/)
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.
**Warning:** Both EaglerXBungee and EaglerXVelocity perform a lot of reflection that will inevitably break after a while when BungeeCord or Velocity is updated upstream. Both plugins will display the precise build number of BungeeCord and Velocity that has been tested by the developers and known to be compatible with EaglerXBungee and EaglerXVelocity when the proxy first starts up. If you are experiencing issues, try checking the BungeeCord or Velocity website for old versions and find the closest version number to whatever the current compatible version number is that is printed by EaglerXBungee/EaglerXVelocity, it will probably fix whatever missing functions the error messages are complaining about.
### 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 ## Launch Options

View File

@ -1,87 +0,0 @@
# EaglercraftXBukkitAPI
### [`gateway/EaglercraftXBukkitAPI/EaglercraftXBukkitAPI-Latest.jar`](gateway/EaglercraftXBukkitAPI/EaglercraftXBukkitAPI-Latest.jar)
### "EaglerXBukkitAPI" is a Bukkit plugin and protocol to allow Bukkit plugins to easily communicate with an EaglerXBungee or EaglerXVelocity instance, its purpose is to provide a standardized interface for creating complex Bukkit plugins that tightly integrate with EaglercraftX features, that would otherwise require developers to also create a BungeeCord/Velocity plugin to act as a "middle man" for sending certain packets or listening for certain events.
## Compiling EaglerXBukkitAPI
Minimum JDK version is 8, since what we are doing here is actually fully within the limitations of the official Bukkit API, we have actually set up Gradle correctly for once. No dependencies have to be downloaded manually, just run `gradlew jar` and you are done.
## Configuration Files
EaglerXBukkitAPI has no configuration files, however you must to edit `settings.yml` on EaglerXBungee/EaglerXVelocity to add or change `enable_backend_rpc_api` to `true` first in order to enable it.
## Initializing Connections
EaglerXBukkitAPI uses Google Commons (Guava) futures in order to implement a system that is similar to JavaScript promises to asynchronously complete tasks without blocking any threads. You should never block and wait for a task to complete synchronously, use the `addCallback` function with an anonymous inner class instead. Here is an example of how to correctly initialize an EaglerXBukkitAPI connection:
```java
// import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
@EventHander
public void onPlayerJoinEvent(PlayerJoinEvent evt) {
IEaglerXBukkitAPI.createAPI(evt.getPlayer())
.addCallback(new FutureCallback<IEaglerXBukkitAPI>() {
@Override
public void onSuccess(IEaglerXBukkitAPI result) {
// Do stuff with result
}
@Override
public void onFailure(Throwable t) {
// Something went wrong
}
});
}
```
You can call `createAPI` as many times as you want, subsequent calls will return the existing `IEaglerXBukkitAPI` handle instead of creating a new one.
## Making a Request
Here is a very simple example of using EaglerXBukkitAPI to determine the name of the website (origin) a player is using, assuming you've already used `createAPI` to initialize a new connection:
```java
// ...
public void onSuccess(IEaglerXBukkitAPI apiObj) {
apiObj.requestPlayerOrigin().addCallback(new FutureCallback<ResponseString>() {
@Override
public void onSuccess(ResponseString result) {
System.out.println("Player's origin is: " + result.string);
}
@Override
public void onFailure(Throwable t) {
// Something went wrong
}
});
}
```
## Event Listeners
Here is an example of how to use EaglerXBukkitAPI to remotely listen for server info webview events on an EaglerXBungee/EaglerXVelocity instance from a Bukkit plugin:
```java
// ...
public void onSuccess(IEaglerXBukkitAPI apiObj) {
apiObj.addEventListener(EnumSubscribeEvents.EVENT_WEBVIEW_OPEN_CLOSE,
new IEaglerRPCEventListener<EventWebViewOpenClose>() {
public void handleEvent(IEaglerXBukkitAPI api, EnumSubscribeEvents eventType,
EventWebViewOpenClose eventData) {
// Handle open/close events
}
});
apiObj.addEventListener(EnumSubscribeEvents.EVENT_WEBVIEW_MESSAGE,
new IEaglerRPCEventListener<EventWebViewMessage>() {
public void handleEvent(IEaglerXBukkitAPI api, EnumSubscribeEvents eventType,
EventWebViewMessage eventData) {
// Handle messages
}
});
apiObj.subscribeEvents(
EnumSubscribeEvents.EVENT_WEBVIEW_OPEN_CLOSE,
EnumSubscribeEvents.EVENT_WEBVIEW_MESSAGE
);
}
```
The rest of EaglerXBukkitAPI should be self explanatory, its just another EaglerXBungeeAPIHelper/EaglerXVelocityAPIHelper.

View File

@ -1,209 +0,0 @@
# EaglercraftXBungee
### [`gateway/EaglercraftXBungee/EaglerXBungee-Latest.jar`](gateway/EaglercraftXBungee/EaglerXBungee-Latest.jar)
### "EaglerXBungee" is a plugin that allows the EaglercraftX 1.8 client to join BungeeCord servers, with an optional authentication system if online-mode is enabled. This is not a setup guide, this document is intended to be used as reference for EaglerXBungee's configuration files and provide some surface-level information meant for plugin developers.
**Warning:** Both EaglerXBungee and EaglerXVelocity perform a lot of reflection that will inevitably break after a while when BungeeCord or Velocity is updated upstream. Both plugins will display the precise build number of BungeeCord and Velocity that has been tested by the developers and known to be compatible with EaglerXBungee and EaglerXVelocity when the proxy first starts up. If you are experiencing issues, try checking the BungeeCord or Velocity website for old versions and find the closest version number to whatever the current compatible version number is that is printed by EaglerXBungee/EaglerXVelocity, it will probably fix whatever missing functions the error messages are complaining about.
## Compiling EaglerXBungee
Minimum JDK version is 8, as of 1.3.0 we are finally using Gradle to compile EaglerXBungee instead of compiling it all manually, however you still need to manually download the latest version of BungeeCord and name it "BungeeCord.jar" and place it in the `deps` folder first before you continue. We just don't care enough to actually use Gradle correctly to download all the dependencies automatically when they are all subject to change at any time as BungeeCord recieves updates upstream. Use the "jar" task to automatically compile the EaglerXBungee JAR file.
## Configuration Files
### NOTE: Currently, the plugin does NOT automatically update config files, if you can't find an option in one of the configuration files documented here, you most likely need to add it to the file yourself!
**The default contents of the config files for EaglerXBungee are stored in [`gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config`](gateway/EaglercraftXBungee/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/config)**
### `settings.yml`
The settings.yml file is primarily used for configuring the built-in skin and cape service and certain connection options.
- **`server_name:`** String, default value is `'EaglercraftXBungee Server'`, sets the name of this EaglercraftX server that is sent with query responses and used for the default "404 websocket upgrade failure" page.
- **`server_uuid:`** String, default value is randomized, sets the UUID of this EaglercraftX server to send with query responses, has no official uses outside of server lists.
- **`websocket_connection_timeout:`** Number, default value is `15000` milliseconds, sets how long a WebSocket connection can last without a ping before being disconnected.
- **`websocket_handshake_timeout:`** Number, default value is `5000` milliseconds, sets how long a connection can sit in the handshake phase before being disconnected.
- **`builtin_http_server_timeout:`** Number, default value is `10000` milliseconds, sets how long an HTTP request to the built-in HTTP server can remain open before being forcefully disconnected.
- **`http_websocket_compression_level:`** Number, default value is `6`, sets the ZLIB compression level (0-9) to use for compressing websocket frames, set to 0 to disable if HTTP compression is already handled through a reverse proxy. You almost definitely need some level of compression for the game to be playable on WiFi networks.
- **`download_vanilla_skins_to_clients:`** Boolean, default value is `true`, sets if the server should download the textures of custom skulls and skins of vanilla online-mode players from Mojang's servers to cache locally and send to all EaglercraftX clients on the server that attempt to render them.
- **`valid_skin_download_urls:`** List of strings, default includes only `'textures.minecraft.net'`, sets the allowed domains to download custom skulls and skins from that are requested by EaglercraftX clients, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`uuid_lookup_ratelimit_player:`** Integer, default value is `50`, limit of how many Mojang API UUID-to-profile lookups a single player is allowed to trigger per minute, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`uuid_lookup_ratelimit_global:`** Integer, default value is `175`, limit of how many Mojang API UUID-to-profile lookups the entire server is allowed to perform per minute, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_download_ratelimit_player:`** Integer, default value is `1000`, limit of how many texture downloads a single player is allowed to trigger per minute, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_download_ratelimit_global:`** Integer, default value is `30000`, limit of how many texture downloads the entire server is allowed to perform per minute, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_db_uri:`** String, default value is `'jdbc:sqlite:eaglercraft_skins_cache.db'`, can be used to change the location of the SQLite database used as a cache for skins and profiles, or to make the server use an entirely different SQL database like MySQL to store the data instead, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_keep_objects_days:`** Integer, default value is `45`, sets the max age for textures (skin files) stored in the skin cache database, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_keep_profiles_days:`** Integer, default value is `7`, sets the max age for player profiles stored in the skin cache database, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_max_objects:`** Integer, default value is `32768`, sets the max number of textures (skin files) stored in the skin cache database before the oldest textures begin to be deleted, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_max_profiles:`** Integer, default value is `32768`, sets the max number of player profiles stored in the skin cache database before the oldest profiles begin to be deleted, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_antagonists_ratelimit:`** Integer, default value is `15`, sets the lockout limit for failing skin lookup requests, intended to reduce the effectiveness of some of the more simplistic types denial of service attacks that skids may attempt to perform on the skin download system, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`sql_driver_class:`** String, default value is `'internal'`, which is currently evaluated to `'org.sqlite.JDBC'`, can be used to set the name of the JDBC driver class to use for connecting to the `skin_cache_db_uri` database
- **`sql_driver_path:`** String, default value is `'internal'`, can be used to set the name of the external JAR file where the JDBC driver class to use for connecting to the `skin_cache_db_uri` database can be found, the default `'internal'` value downloads the sqlite-jdbc JAR from maven and loads it automatically, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`eagler_players_vanilla_skin:`** String, default value is `''` but was originally `'lax1dude'`, can be used to set the skin to apply to EaglercraftX players when a player on Minecraft Java Edition sees them in game. The value is the username of a premium Minecraft account to use the skin from. You cannot use a local PNG file due to the profile signature requirements in vanilla Minecraft clients.
- **`enable_is_eagler_player_property:`** Boolean, default value is `true`, can be used to control if the `isEaglerPlayer` GameProfile property should be added to EaglercraftX players, this property is used to ensure that EaglercraftX players always only display their custom skins when viewed by another EaglercraftX players on the server instead of showing the skin attached to their Java Edition username, but this property also cause plugins like ViaVersion to crash.
- **`disable_voice_chat_on_servers:`** List of strings, default value is nothing (`[]`), contains a list of names of registered servers on your BungeeCord proxy that voice chat should show up as "disabled" on. Note that to disable voice globally you should modify `listeners.yml` instead.
- **`disable_fnaw_skins_everywhere:`** Boolean, default value is `false`, can be used to globally disable FNAW skins if your players bitch about them a lot and are too lazy to just disable the FNAW skins locally on their clients.
- **`disable_fnaw_skins_on_servers:`** List of strings, default value is nothing (`[]`), contains a list of names of registered servers on your BungeeCord proxy that the FNAW skins should be disabled on. Good for explicitly disabling them for PVP but allowing them everywhere else.
- **`enable_backend_rpc_api:`** Boolean, default value is `false`, if support for servers running the EaglerXBukkitAPI plugin should be enabled or not.
- **`use_modernized_channel_names:`** Boolean, default value is `false`, if "modernized" plugin channel names compatible with Minecraft 1.13+ should be used for EaglerXBukkitAPI plugin message packets
### `listeners.yml`
Defines one or more "listeners" (open ports) for EaglercraftX players to use to join the server. Each listener supports the following configuration options, a lot of which you will already be familiar with if you've ever set up a BungeeCord for a Java Edition server before:
- **`address:`** String, default value is `0.0.0.0:8081`, sets the primary IPv4/port for EaglerXBungee to listen on.
- **`address_v6:`** String, default value is `'null'`, sets the primary IPv6/port for EaglerXBungee to listen on.
- **`max_players:`** Integer, default value is `60`, sets the maximum number of players that can join the server through this listener, set to `-1` to disable the limit.
- **`tab_list:`** String, default value is `GLOBAL_PING`, sets the option with the same name on the underlying BungeeCord listener, currently not used by EaglercraftX in any way.
- **`default_server:`** String, default value is `lobby`, sets the name of the default server for players to be sent to when they first connect to this listener.
- **`force_default_server:`** Boolean, default value is `false`, sets if players should always be connected to `default_server` when they connect to this listener.
- **`forward_ip:`** Boolean, default value is `false`, sets if connections to this listener will use an HTTP header to forward the player's real IP address from a reverse proxy (or CloudFlare) to the BungeeCord server. This is required for EaglerXBungee's rate limiting and a lot of plugins to work correctly if they are used behind a reverse HTTP proxy or CloudFlare.
- **`forward_ip_header:`** String, default value is `X-Real-IP`, sets the name of the request header that contains the player's real IP address if the `forward_ip` option is enabled. This option is commonly set to `X-Forwarded-For` or `CF-Connecting-IP` for a lot of server setups.
- **`redirect_legacy_clients_to:`** String, default value is `'null'`, sets the WebSocket address to redirect legacy Eaglercraft 1.5.2 clients to if they mistakenly try to join the server through this listener.
- **`server_icon:`** String, default value is `server-icon.png`, sets the name of the 64x64 PNG file to display as this listener's server icon, relative to the working directory of the BungeeCord proxy server.
- **`server_motd:`** List of up to 2 strings, default value is `'&6An EaglercraftX server'`, sets the contents of the listener's MOTD, which is the text displayed along with the `server_icon` when players add this server's listener address to their client's Multiplayer menu server list.
- **`allow_motd:`** Boolean, default value is `true`, if this listener should respond to MOTD queries or not.
- **`allow_query:`** Boolean, default value is `true`, if this listener should respond to all other types of queries or not.
- **`min_minecraft_protocol:`** Integer, default value is `47`, sets the minimum Minecraft [protocol version](https://wiki.vg/Protocol_version_numbers) that EaglercraftX-based clients are allowed to connect with (`47` = 1.8)
- **`max_minecraft_protocol:`** Integer, default value is `340`, sets the maximum Minecraft protocol version that EaglercraftX-based clients are allowed to connect with (`340` = 1.12.2)
- **`allow_protocol_v3:`** Boolean, default value is `true`, if this listener should allow clients using the v1/v2/v3 protocols to join (pre-u37 clients).
- **`allow_protocol_v4:`** Boolean, default value is `true`, if this listener should allow clients using the v4 protocol to join (post-u37 clients).
- **`protocol_v4_defrag_send_delay:`** Integer, default value is `10`, the number of milliseconds to wait before flushing all pending EaglercraftX plugin message packets, saves bandwidth by combining multiple messages into a single plugin message packet. Setting this to `0` has the same effect on clientbound packets as setting `eaglerNoDelay` to `true` does on a post-u37 client for all serverbound packets.
- **`use_haproxy_protocol:`** Boolean, default value is `false`, can be used to enable support for the HAProxy proxy protocol. Make sure to also add the `check`, `check-send-proxy`, and `send-proxy-v2` parameters to your `server` directives in the HAProxy config file.
- **`allow_cookie_revoke_query:`** Boolean, default value is `true`, If this listener should accept queries from post-u37 clients to revoke session tokens, you need to create your own BungeeCord plugin to go with EaglerXBungee that handles the `EaglercraftRevokeSessionQueryEvent` event it fires in order for this feature to work correctly.
- **`request_motd_cache:`** Section that defines caching hints for server lists that cache the MOTD via the `MOTD.cache` query. As far as we know, not even the official Eaglercraft Server List on eaglercraft.com currently pays attention to these hints or attempts to cache MOTDs, so they can be ignored for now.
- **`cache_ttl:`** Integer, default value is `7200`, sets how many seconds for the server list to store the MOTD in cache.
- **`online_server_list_animation:`** Boolean, default is `false`, if the MOTD should be cached in an "animated format" that is yet to be standardized.
- **`online_server_list_results:`** Boolean, default is `true`, if the MOTD should be cached when shown in search results.
- **`online_server_list_trending:`** Boolean, default is `true`, if the MOTD should be cached if the server makes it to the top of the homepage.
- **`online_server_list_portfolios:`** Boolean, default is `false`, if the MOTD should be cached when viewing more details about the specific server.
- **`http_server:`** Section that defines settings for the integrated HTTP server, used to make the listener behave as a normal HTTP server when a non-WebSocket request is recieved (like when the listener address is entered into a browser's address bar). These options can be used to replace the "404 WebSocket Upgrade Failure" message with a custom HTML file instead.
- **`enabled:`** Boolean, default value is `false`, if this is set to true then the default "404 WebSocket Upgrade Failure" page will be disabled and replaced with the integrated file-based HTTP server, perfect for hosting a copy of the EaglercraftX client.
- **`root:`** String, default value is `web`, sets the folder that contains the HTTP server's document root, this is relative to the `plugins/EaglercraftXBungee` folder where the config files are stored.
- **`page_404_not_found:`** String, default value is `'default'`, can be used to replace the HTTP server's 404 page.
- **`page_index_name:`** List of strings, default values are `'index.html'` and `'index.htm'`, can be used to specify the name of index.html.
- **`allow_voice:`** Boolean, default is `false`, sets if voice should show up as "disabled" for players using this listener. Voice is not recommended for public servers since little to no consideration was given to actually validating the contents of signaling packets sent between clients.
- **`ratelimit:`** Section containing rate limiting configurations for several different connection types.
- **`ip:`** Global ratelimit imposed on all connection types.
- **`login:`** Sets ratelimit on login (server join) attempts.
- **`motd:`** Sets ratelimit on MOTD query types.
- **`query:`** Sets ratelimit on all other query types.
- **`enable:`** If the rate limit (ip/login/motd/query) should be enabled.
- **`period:`** Sets the period in the number of seconds.
- **`limit:`** Sets the number of requests a single IP address can send in `period` seconds before being limited.
- **`limit_lockout:`** Sets the number of requests a single IP address can send in `period` seconds before being locked out.
- **`lockout_duration:`** Sets the total number of seconds a "lock out" should last on this limiter.
### `authservice.yml`
The authservice.yml file is used for configuring the built-in online mode authentication service included with the plugin or to integrate with a 3rd party authentication system provided by another plugin.
- **`enable_authentication_system:`** Boolean, default is `true`, if the events for the authentication protocol should be enabled.
- **`use_onboard_eaglerx_system:`** Boolean, default is `true`, if the built-in online mode authentication system should be enabled.
- **`auth_db_uri:`** String, default value is `'jdbc:sqlite:eaglercraft_auths.db'`, can be used to change the location of the SQLite database used for the built-in online mode authentication system, or to make the server use an entirely different SQL database like MySQL to store the data instead.
- **`sql_driver_class:`** String, default value is `'internal'`, see the description of the `settings.yml` option with the same name.
- **`sql_driver_path:`** String, default value is `'internal'`, see the description of the `settings.yml` option with the same name.
- **`password_prompt_screen_text:`** String, default value is `'Enter your password to join:'`, text displayed on the EaglercraftX client's password screen when joining the server with the built-in online mode authentication system.
- **`wrong_password_screen_text:`** String, default value is `'Password Incorrect!'`, text displayed if the wrong password is entered on the EaglercraftX client's password screen when joining the server with the built-in online mode authentication system.
- **`not_registered_screen_text:`** String, default value is `'You are not registered on this server!'`, text displayed when joining the server with the built-in online mode authentication system when using an account that has not been registered.
- **`eagler_command_name:`** String, default value is `'eagler'`, the name of the command to use for registering and/or logging in when joining the server with the built-in online mode authentication system.
- **`use_register_command_text:`** String, default value is `'&aUse /eagler to set an Eaglercraft password on this account'`, localization for when players use the `/eagler` command on the server.
- **`use_change_command_text:`** String, default value is `'&bUse /eagler to change your Eaglercraft password'`, localization for when players use the `/eagler` command on the server.
- **`command_success_text:`** String, default value is `'&bYour eagler password was changed successfully.'`, localization for when players use the `/eagler` command on the server.
- **`last_eagler_login_message:`** String, default value is `'Your last Eaglercraft login was on $date from $ip'`, localization for when players join the server with the built-in online mode authentication system.
- **`too_many_registrations_message:`** String, default value is `'&cThe maximum number of registrations has been reached for your IP address'`, localization for when players use the `/eagler` command on the server.
- **`need_vanilla_to_register_message:`** String, default value is `'&cYou need to log in with a vanilla account to use this command'`, localization for when players use the `/eagler` command on the server.
- **`override_eagler_to_vanilla_skins:`** Boolean, default value is `false`, if players who join the server after registering with an online mode account should show the same skin as the online-mode account they registered with.
- **`max_registration_per_ip:`** Integer, default value is `-1`, if greater than 0 it specifies the max number of accounts that can be created per IP address on the server with the built-in online mode authentication system.
### `ice_servers.yml`
The ice_servers.yml file is used for configuring the set of STUN/TURN servers that clients on this server should use for voice chat. Beware the default "openrelayproject" TURN servers are no longer active as of 2024, most likely as a result of being the default ond only TURN servers shipped with every copy of Eaglercraft to ever use WebRTC in some way.
- **`voice_servers_no_passwd:`** List of strings, defines a set of STUN/TURN server URIs to use that don't require a username and password.
- **`voice_servers_passwd:`** Section of sections, defines a set of STUN/TURN server URIs to use that do require a username and password, along with the username and password to use with each one.
### `updates.yml`
The updates.yml file is used for configuring the decentralized and totally legal update system used by EaglercraftX clients.
- **`block_all_client_updates:`** Boolean, default value is `false`, can be used to completely disable the update system.
- **`discard_login_packet_certs:`** Boolean, default value is `false`, can be used to prevent the server from relaying random crowdsourced update certificates that were recieved from players who joined the server using signed clients.
- **`cert_packet_data_rate_limit:`** Integer, default value is `524288`, can be used to set the global rate limit for how many bytes per second of certificates the server should send to all players.
- **`enable_eagcert_folder:`** Boolean, default value is `true`, can be used to enable or disable the "eagcert" folder used for distributing specific certificates as locally provided .cert files
- **`download_latest_certs:`** Boolean, default value is `true`, can be used to automaticlly download the latest certificates to the "eagcert" folder
- **`download_certs_from:`** List of strings, defines the URLs to download the certificates from if `download_latest_certs` is enabled
- **`check_for_update_every:`** Integer, default value is `28800` seconds, defines how often to check the URL list for updated certificates
### `pause_menu/pause_menu.yml`
For EaglercraftX u37 and up, can be used for changing the appearance of the pause menu and a few other screens with custom icons and strings, also used for enabling the "Server Info" webview and configuring its contents.
- **`enable_custom_pause_menu:`** Boolean, default value is `false`, if pause menu customization should be enabled on supported clients or not
- **`server_info_button:`** Section, defines properties of the "Server Info" button, which is always hidden unless pause menu customization is enabled
- **`enable_button:`** Boolean, default value is `true`, if the button should be shown or not
- **`button_text:`** String, default value is `'Server Info'`, the text to display on the button, useful if you want to use this feature for something other than a "Server Info" button
- **`button_mode_open_new_tab:`** Boolean, default value is `false`, can be used to make the "Server Info" button act as a hyperlink that opens a URL in a new tab instead of displaying content in an embedded webview iframe in the client.
- **`server_info_embed_url:`** String, default value is `''`, sets the URL for the "Server Info" button to use if it should open a URL in a new tab or the webview instead of directly downloading the markup to display from the EaglerXBungee server itself over the WebSocket.
- **`button_mode_embed_file:`** Boolean, default value is `true`, determines if the "Server Info" button should download the webview markup directly from the EaglerXBungee server over WebSocket instead of loading an external URL. Cannot be used with `button_mode_open_new_tab`!
- **`server_info_embed_file:`** String, default value is `'server_info.html'`, sets the name of the local file/template containing the markup to display in the "Server Info" webview if it is not in URL mode.
- **`server_info_embed_screen_title:`** String, default value is `'Server Info'`, sets the title string of the screen that displays the webview.
- **`server_info_embed_send_chunk_rate:`** Integer, default value is `1`, defines how many chunks of server info data to send per 250ms when downloading the server info markup to a client.
- **`server_info_embed_send_chunk_size:`** Integer, default value is `24576`, defines the size of each chunk of server info data when it is being downloaded to a client.
- **`enable_template_macros:`** Boolean, default value is `true`, if the server info markup should be processed for any eagler template macros (defined like `` {% arg1 `arg2` ... %} ``)
- **`server_info_embed_template_globals:`** Section, defines a list of additional global variables to use with the template processor
- **`allow_embed_template_eval_macro:`** Boolean, default value is `false`, if the template processor should allow the "eval" macro to be used in the server info markup file (not to be confused with the JavaScript function, although there is never a good reason to use JavaScript's eval function in your code either)
- **`enable_webview_javascript:`** Boolean, default value is `false`, if the server info webview should allow JavaScript to be executed or not. This will display an "allow JavaScript" screen to your players the first time they attempt to view it.
- **`enable_webview_message_api:`** Boolean, default value is `false`, if the server info webview has JavaScript enabled and should be permitted to open a message channel back to your EaglerXBungee server to exchange arbitrary message packets. This can be used, for example, to implement a dynamic menu on your server using JavaScript and HTML that players can access through the server info webview that integrates directly with your gamemodes.
- **`enable_webview_strict_csp:`** Boolean, default value is `true`, if the `csp` attribute on the webview iframe should be set or not for added security, beware this is not supported on all browsers and will be silently disabled when the client detects it as unsupported.
- **`discord_button:`** Section, can be used to turn the "Invite" (formerly "Open to LAN") button on the pause menu into a "Discord" button that players can click to join your discord server
- **`enable_button:`** Boolean, default value is `true`, sets if the discord button should be enabled or not
- **`button_text:`** String, default value is `'Discord'`, sets the text that should be displayed on the button
- **`button_url:`** String, default value is `'https://invite url here'`, defines the URL to open when the button is pressed
- **`custom_images:`** Section, can be used to add icons to certain buttons, change the backgrounds of some screens, and add watermarks of your server to the inventory and pause menu and such if you're into that. For the best GPU compatibility, use only textures with powers of 2 as dimensions (such as 32x32 pixels), the width and height do not need to match, they just both need to be a power of 2. There is also a limit on the maximum size, icons can be no larger than 255x255 pixels (Effective max power-of-2 is 128x128). Color values will be downsampled to 16bpp and use a magic value to represent transparent pixels when the pause menu customization packet is sent to a client.
- **`icon_title_L:`** String, default value is `''`, sets the icon to show on the left side of the pause menu screen's title
- **`icon_title_R:`** String, default value is `''`, sets the icon to show on the right side of the pause menu screen's title
- **`icon_backToGame_L:`** String, default value is `''`, sets the icon to show on the left side of the "Back to Game" button
- **`icon_backToGame_R:`** String, default value is `''`, sets the icon to show on the right side of the "Back to Game" button
- **`icon_achievements_L:`** String, default value is `''`, sets the icon to show on the left side of the "Achievements" button
- **`icon_achievements_R:`** String, default value is `''`, sets the icon to show on the right side of the "Achievements" button
- **`icon_statistics_L:`** String, default value is `''`, sets the icon to show on the left side of the "Statistics" button
- **`icon_statistics_R:`** String, default value is `''`, sets the icon to show on the right side of the "Statistics" button
- **`icon_serverInfo_L:`** String, default value is `''`, sets the icon to show on the left side of the server info button
- **`icon_serverInfo_R:`** String, default value is `''`, sets the icon to show on the right side of the server info button
- **`icon_options_L:`** String, default value is `''`, sets the icon to show on the left side of the "Options" button
- **`icon_options_R:`** String, default value is `''`, sets the icon to show on the right side of the "Options" button
- **`icon_discord_L:`** String, default value is `''`, sets the icon to show on the left side of the discord button
- **`icon_discord_R:`** String, default value is `''`, sets the icon to show on the right side of the discord button
- **`icon_disconnect_L:`** String, default value is `''`, sets the icon to show on the left side of the "Disconnect" button
- **`icon_disconnect_R:`** String, default value is `''`, sets the icon to show on the right side of the "Disconnect" button
- **`icon_background_pause:`** String, default value is `'test_image.png'`, sets the icon to show as a repeating pattern in the background of the pause menu and related screens. It is especially important for GPU compatibility for this one to be a power-of-2 sized texture.
- **`icon_background_all:`** String, default value is `'test_image.png'`, sets the icon to show as a repeating pattern in the background of all other screens in the game. It is especially important for GPU compatibility for this one to be a power-of-2 sized texture.
- **`icon_watermark_pause:`** String, default value is `''`, sets a watermark to show in the bottom-left corner of the pause menu
- **`icon_watermark_all:`** String, default value is `''`, sets a watermark to show in the bottom-left corner of all other screens in the game
## Event Types
The events added by EaglerXBungee are located in the [`net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event`](gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/api/event) package and can be listened for the same way as you would for a regular BungeeCord event. When an EaglercraftX 1.8 player joins your server, all the regular BungeeCord login events are fired by EaglerXBungee to maintain compatibility with other existing BungeeCord plugins, however EaglerXBungee also adds several of its own event types to allow additional Eaglercraft specific features to be accessible through the main BungeeCord event bus as well.
- **`EaglercraftWebSocketOpenEvent`** Event that is fired when a new WebSocket connection is first opened to the server (regardless if its a query or actual player login attempt) useful for quickly filtering out new connections based on a specific origin or user agent header.
- **`EaglercraftClientBrandEvent`** Event that is fired when an EaglercraftX player joins, it contains the Eaglercraft client's "brand" and "version" strings along with the origin and user agent headers that can be used to detect 90% of the currently existing skid clients thanks to lax1dude making the base client self-snitch these commonly modified strings.
- **`EaglercraftIsAuthRequiredEvent`** Event that is fired when an EaglercraftX player attempts to join the server while the authentication system is enabled and `use_onboard_eaglerx_system` is `false`, used for implementing custom authentication systems.
- **`EaglercraftHandleAuthPasswordEvent`** Event that is fired when an EaglercraftX player enters a password into the "Authentication Required" screen while the authentication system is enabled and `use_onboard_eaglerx_system` is `false`, used for implementing custom authentication systems.
- **`EaglercraftHandleAuthCookieEvent`** Event that is fired when an EaglercraftX player joins the server with cookies set and while authentication system is enabled and `use_onboard_eaglerx_system` is `false`, you must set cookie auth as allowed while handling "EaglercraftIsAuthRequiredEvent" first for this event to actually be fired, used for implementing custom authentication systems that use cookies to store a session token for auto login.
- **`EaglercraftRevokeSessionQueryEvent`** Event that is fired when a player uses the "Revoke Session Token" feature in a u37 client to invalidate a cookie that was set on their client with the "revoke query supported" bit. Make sure to enable session revoke queries in listeners.yml!
- **`EaglercraftRegisterSkinEvent`** Event that is fired when an EaglercraftX player's skin is recieved, can be used to analyze or modify or replace the skin with a different texture or preset ID if needed. Note that players using pre-u37 clients may not see the modified/replaced skin.
- **`EaglercraftRegisterCapeEvent`** Event that is fired when an EaglercraftX player's cape is recieved, can be used to analyze or modify or replace the cape with a different texture or preset ID if needed. Use preset ID 0 to disable their cape entirely. Note that players using pre-u37 clients may not see the modified/replaced cape.
- **`EaglercraftMOTDEvent`** Event that is fired when a MOTD query request is recieved, used for implement a custom server MOTD handler, or implementing an animated MOTD like the EaglerMOTD plugin.
- **`EaglercraftVoiceStatusChangeEvent`** Event that is fired when `allow_voice` is enabled and a player transitions between voice states (SERVER_DISABLE, DISABLED, ENABLED) cannot be cancelled so it is mostly just useful for logging or displaying some "Rules" in chat.
- **`EaglercraftWebViewChannelEvent`** Event that is fired when the server info webview is open and JavaScript is enabled and the webview opens/closes a new message channel to EaglerXBungee.
- **`EaglercraftWebViewMessageEvent`** Event that is fired when the server info webview is open and JavaScript is enabled and the webview has already opened a channel to EaglerXBungee and a new message is recieved on that open channel.
## Registering Queries
If you would like to add your own custom `Accept:` query handlers to the proxy (along with MOTD, version, and session revoke) you can register them at startup using the register functions provided by the [`EaglerQueryHandler`](gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/api/query/EaglerQueryHandler.java) class.
## EaglerXBungeeAPIHelper
To help make plugin development easier, a class called [`EaglerXBungeeAPIHelper`](gateway/EaglercraftXBungee/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_bungeecord/api/EaglerXBungeeAPIHelper.java) is included that defines dozens of helper functions for easily and safely interacting with EaglercraftX clients. This is to enable developers to program plugins for EaglerXBungee servers with minimal knowledge of the actual underlying protocol used by the client for skins and capes and voice and other exclusive features. Its recommended to convert all your existing code to use the `EaglerXBungeeAPIHelper` instead of whatever packet hacks you were doing before it was added when you migrate your network to EaglerXBungee 1.3.0+. All your existing packet hacks will be broken anyway unless you configure the server to force all u37+ clients to use protocol V3 instead.

View File

@ -1,206 +0,0 @@
# EaglercraftXVelocity
### [`gateway/EaglercraftXVelocity/EaglerXVelocity-Latest.jar`](gateway/EaglercraftXVelocity/EaglerXVelocity-Latest.jar)
### "EaglerXVelocity" is a plugin that allows the EaglercraftX 1.8 client to join Velocity servers, with an optional authentication system if online-mode is enabled. This is not a setup guide, this document is intended to be used as reference for EaglerXVelocity's configuration files and provide some surface-level information meant for plugin developers.
**Warning:** Both EaglerXBungee and EaglerXVelocity perform a lot of reflection that will inevitably break after a while when BungeeCord or Velocity is updated upstream. Both plugins will display the precise build number of BungeeCord and Velocity that has been tested by the developers and known to be compatible with EaglerXBungee and EaglerXVelocity when the proxy first starts up. If you are experiencing issues, try checking the BungeeCord or Velocity website for old versions and find the closest version number to whatever the current compatible version number is that is printed by EaglerXBungee/EaglerXVelocity, it will probably fix whatever missing functions the error messages are complaining about.
## Compiling EaglerXVelocity
Minimum JDK version is 17, as of 1.1.0 we are finally using Gradle to compile EaglerXVelocity instead of compiling it all manually, however you still need to manually download the latest version of Velocity and name it "Velocity.jar" and place it in the `deps` folder first before you continue. We just don't care enough to actually use Gradle correctly to download all the dependencies automatically when they are all subject to change at any time as Velocity recieves updates upstream. Use the "jar" task to automatically compile the EaglerXVelocity JAR file.
## Configuration Files
### NOTE: Currently, the plugin does NOT automatically update config files, if you can't find an option in one of the configuration files documented here, you most likely need to add it to the file yourself!
**The default contents of the config files for EaglerXVelocity are stored in [`gateway/EaglercraftXVelocity/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/config`](gateway/EaglercraftXVelocity/src/main/resources/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/config)**
### `settings.yml`
The settings.yml file is primarily used for configuring the built-in skin and cape service and certain connection options.
- **`server_name:`** String, default value is `'EaglercraftXVelocity Server'`, sets the name of this EaglercraftX server that is sent with query responses and used for the default "404 websocket upgrade failure" page.
- **`server_uuid:`** String, default value is randomized, sets the UUID of this EaglercraftX server to send with query responses, has no official uses outside of server lists.
- **`websocket_connection_timeout:`** Number, default value is `15000` milliseconds, sets how long a WebSocket connection can last without a ping before being disconnected.
- **`websocket_handshake_timeout:`** Number, default value is `5000` milliseconds, sets how long a connection can sit in the handshake phase before being disconnected.
- **`builtin_http_server_timeout:`** Number, default value is `10000` milliseconds, sets how long an HTTP request to the built-in HTTP server can remain open before being forcefully disconnected.
- **`http_websocket_compression_level:`** Number, default value is `6`, sets the ZLIB compression level (0-9) to use for compressing websocket frames, set to 0 to disable if HTTP compression is already handled through a reverse proxy. You almost definitely need some level of compression for the game to be playable on WiFi networks.
- **`download_vanilla_skins_to_clients:`** Boolean, default value is `true`, sets if the server should download the textures of custom skulls and skins of vanilla online-mode players from Mojang's servers to cache locally and send to all EaglercraftX clients on the server that attempt to render them.
- **`valid_skin_download_urls:`** List of strings, default includes only `'textures.minecraft.net'`, sets the allowed domains to download custom skulls and skins from that are requested by EaglercraftX clients, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`uuid_lookup_ratelimit_player:`** Integer, default value is `50`, limit of how many Mojang API UUID-to-profile lookups a single player is allowed to trigger per minute, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`uuid_lookup_ratelimit_global:`** Integer, default value is `175`, limit of how many Mojang API UUID-to-profile lookups the entire server is allowed to perform per minute, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_download_ratelimit_player:`** Integer, default value is `1000`, limit of how many texture downloads a single player is allowed to trigger per minute, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_download_ratelimit_global:`** Integer, default value is `30000`, limit of how many texture downloads the entire server is allowed to perform per minute, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_db_uri:`** String, default value is `'jdbc:sqlite:eaglercraft_skins_cache.db'`, can be used to change the location of the SQLite database used as a cache for skins and profiles, or to make the server use an entirely different SQL database like MySQL to store the data instead, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_keep_objects_days:`** Integer, default value is `45`, sets the max age for textures (skin files) stored in the skin cache database, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_keep_profiles_days:`** Integer, default value is `7`, sets the max age for player profiles stored in the skin cache database, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_max_objects:`** Integer, default value is `32768`, sets the max number of textures (skin files) stored in the skin cache database before the oldest textures begin to be deleted, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_max_profiles:`** Integer, default value is `32768`, sets the max number of player profiles stored in the skin cache database before the oldest profiles begin to be deleted, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`skin_cache_antagonists_ratelimit:`** Integer, default value is `15`, sets the lockout limit for failing skin lookup requests, intended to reduce the effectiveness of some of the more simplistic types denial of service attacks that skids may attempt to perform on the skin download system, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`sql_driver_class:`** String, default value is `'internal'`, which is currently evaluated to `'org.sqlite.JDBC'`, can be used to set the name of the JDBC driver class to use for connecting to the `skin_cache_db_uri` database
- **`sql_driver_path:`** String, default value is `'internal'`, can be used to set the name of the external JAR file where the JDBC driver class to use for connecting to the `skin_cache_db_uri` database can be found, the default `'internal'` value downloads the sqlite-jdbc JAR from maven and loads it automatically, only relevant if `download_vanilla_skins_to_clients` is enabled.
- **`eagler_players_vanilla_skin:`** String, default value is `''` but was originally `'lax1dude'`, can be used to set the skin to apply to EaglercraftX players when a player on Minecraft Java Edition sees them in game. The value is the username of a premium Minecraft account to use the skin from. You cannot use a local PNG file due to the profile signature requirements in vanilla Minecraft clients.
- **`enable_is_eagler_player_property:`** Boolean, default value is `true`, can be used to control if the `isEaglerPlayer` GameProfile property should be added to EaglercraftX players, this property is used to ensure that EaglercraftX players always only display their custom skins when viewed by another EaglercraftX players on the server instead of showing the skin attached to their Java Edition username, but this property also cause plugins like ViaVersion to crash.
- **`disable_voice_chat_on_servers:`** List of strings, default value is nothing (`[]`), contains a list of names of registered servers on your Velocity proxy that voice chat should show up as "disabled" on. Note that to disable voice globally you should modify `listeners.yml` instead.
- **`disable_fnaw_skins_everywhere:`** Boolean, default value is `false`, can be used to globally disable FNAW skins if your players bitch about them a lot and are too lazy to just disable the FNAW skins locally on their clients.
- **`disable_fnaw_skins_on_servers:`** List of strings, default value is nothing (`[]`), contains a list of names of registered servers on your Velocity proxy that the FNAW skins should be disabled on. Good for explicitly disabling them for PVP but allowing them everywhere else.
- **`enable_backend_rpc_api:`** Boolean, default value is `false`, if support for servers running the EaglerXBukkitAPI plugin should be enabled or not.
- **`use_modernized_channel_names:`** Boolean, default value is `false`, if "modernized" plugin channel names compatible with Minecraft 1.13+ should be used for EaglerXBukkitAPI plugin message packets
### `listeners.yml`
Defines one or more "listeners" (open ports) for EaglercraftX players to use to join the server. Each listener supports the following configuration options, a lot of which you will already be familiar with if you've ever set up a Velocity for a Java Edition server before:
- **`address:`** String, default value is `0.0.0.0:8081`, sets the primary IPv4/port for EaglerXVelocity to listen on.
- **`address_v6:`** String, default value is `'null'`, sets the primary IPv6/port for EaglerXVelocity to listen on.
- **`max_players:`** Integer, default value is `60`, sets the maximum number of players that can join the server through this listener, set to `-1` to disable the limit.
- **`forward_ip:`** Boolean, default value is `false`, sets if connections to this listener will use an HTTP header to forward the player's real IP address from a reverse proxy (or CloudFlare) to the Velocity server. This is required for EaglerXVelocity's rate limiting and a lot of plugins to work correctly if they are used behind a reverse HTTP proxy or CloudFlare.
- **`forward_ip_header:`** String, default value is `X-Real-IP`, sets the name of the request header that contains the player's real IP address if the `forward_ip` option is enabled. This option is commonly set to `X-Forwarded-For` or `CF-Connecting-IP` for a lot of server setups.
- **`redirect_legacy_clients_to:`** String, default value is `'null'`, sets the WebSocket address to redirect legacy Eaglercraft 1.5.2 clients to if they mistakenly try to join the server through this listener.
- **`server_icon:`** String, default value is `server-icon.png`, sets the name of the 64x64 PNG file to display as this listener's server icon, relative to the working directory of the Velocity proxy server.
- **`server_motd:`** List of up to 2 strings, default value is `'&6An EaglercraftX server'`, sets the contents of the listener's MOTD, which is the text displayed along with the `server_icon` when players add this server's listener address to their client's Multiplayer menu server list.
- **`allow_motd:`** Boolean, default value is `true`, if this listener should respond to MOTD queries or not.
- **`allow_query:`** Boolean, default value is `true`, if this listener should respond to all other types of queries or not.
- **`min_minecraft_protocol:`** Integer, default value is `47`, sets the minimum Minecraft [protocol version](https://wiki.vg/Protocol_version_numbers) that EaglercraftX-based clients are allowed to connect with (`47` = 1.8)
- **`max_minecraft_protocol:`** Integer, default value is `340`, sets the maximum Minecraft protocol version that EaglercraftX-based clients are allowed to connect with (`340` = 1.12.2)
- **`allow_protocol_v3:`** Boolean, default value is `true`, if this listener should allow clients using the v1/v2/v3 protocols to join (pre-u37 clients).
- **`allow_protocol_v4:`** Boolean, default value is `true`, if this listener should allow clients using the v4 protocol to join (post-u37 clients).
- **`protocol_v4_defrag_send_delay:`** Integer, default value is `10`, the number of milliseconds to wait before flushing all pending EaglercraftX plugin message packets, saves bandwidth by combining multiple messages into a single plugin message packet. Setting this to `0` has the same effect on clientbound packets as setting `eaglerNoDelay` to `true` does on a post-u37 client for all serverbound packets.
- **`use_haproxy_protocol:`** Boolean, default value is `false`, can be used to enable support for the HAProxy proxy protocol. Make sure to also add the `check`, `check-send-proxy`, and `send-proxy-v2` parameters to your `server` directives in the HAProxy config file.
- **`allow_cookie_revoke_query:`** Boolean, default value is `true`, If this listener should accept queries from post-u37 clients to revoke session tokens, you need to create your own Velocity plugin to go with EaglerXVelocity that handles the `EaglercraftRevokeSessionQueryEvent` event it fires in order for this feature to work correctly.
- **`request_motd_cache:`** Section that defines caching hints for server lists that cache the MOTD via the `MOTD.cache` query. As far as we know, not even the official Eaglercraft Server List on eaglercraft.com currently pays attention to these hints or attempts to cache MOTDs, so they can be ignored for now.
- **`cache_ttl:`** Integer, default value is `7200`, sets how many seconds for the server list to store the MOTD in cache.
- **`online_server_list_animation:`** Boolean, default is `false`, if the MOTD should be cached in an "animated format" that is yet to be standardized.
- **`online_server_list_results:`** Boolean, default is `true`, if the MOTD should be cached when shown in search results.
- **`online_server_list_trending:`** Boolean, default is `true`, if the MOTD should be cached if the server makes it to the top of the homepage.
- **`online_server_list_portfolios:`** Boolean, default is `false`, if the MOTD should be cached when viewing more details about the specific server.
- **`http_server:`** Section that defines settings for the integrated HTTP server, used to make the listener behave as a normal HTTP server when a non-WebSocket request is recieved (like when the listener address is entered into a browser's address bar). These options can be used to replace the "404 WebSocket Upgrade Failure" message with a custom HTML file instead.
- **`enabled:`** Boolean, default value is `false`, if this is set to true then the default "404 WebSocket Upgrade Failure" page will be disabled and replaced with the integrated file-based HTTP server, perfect for hosting a copy of the EaglercraftX client.
- **`root:`** String, default value is `web`, sets the folder that contains the HTTP server's document root, this is relative to the `plugins/EaglercraftXVelocity` folder where the config files are stored.
- **`page_404_not_found:`** String, default value is `'default'`, can be used to replace the HTTP server's 404 page.
- **`page_index_name:`** List of strings, default values are `'index.html'` and `'index.htm'`, can be used to specify the name of index.html.
- **`allow_voice:`** Boolean, default is `false`, sets if voice should show up as "disabled" for players using this listener. Voice is not recommended for public servers since little to no consideration was given to actually validating the contents of signaling packets sent between clients.
- **`ratelimit:`** Section containing rate limiting configurations for several different connection types.
- **`ip:`** Global ratelimit imposed on all connection types.
- **`login:`** Sets ratelimit on login (server join) attempts.
- **`motd:`** Sets ratelimit on MOTD query types.
- **`query:`** Sets ratelimit on all other query types.
- **`enable:`** If the rate limit (ip/login/motd/query) should be enabled.
- **`period:`** Sets the period in the number of seconds.
- **`limit:`** Sets the number of requests a single IP address can send in `period` seconds before being limited.
- **`limit_lockout:`** Sets the number of requests a single IP address can send in `period` seconds before being locked out.
- **`lockout_duration:`** Sets the total number of seconds a "lock out" should last on this limiter.
### `authservice.yml`
The authservice.yml file is used for configuring the built-in online mode authentication service included with the plugin or to integrate with a 3rd party authentication system provided by another plugin.
- **`enable_authentication_system:`** Boolean, default is `true`, if the events for the authentication protocol should be enabled.
- **`use_onboard_eaglerx_system:`** Boolean, default is `true`, if the built-in online mode authentication system should be enabled.
- **`auth_db_uri:`** String, default value is `'jdbc:sqlite:eaglercraft_auths.db'`, can be used to change the location of the SQLite database used for the built-in online mode authentication system, or to make the server use an entirely different SQL database like MySQL to store the data instead.
- **`sql_driver_class:`** String, default value is `'internal'`, see the description of the `settings.yml` option with the same name.
- **`sql_driver_path:`** String, default value is `'internal'`, see the description of the `settings.yml` option with the same name.
- **`password_prompt_screen_text:`** String, default value is `'Enter your password to join:'`, text displayed on the EaglercraftX client's password screen when joining the server with the built-in online mode authentication system.
- **`wrong_password_screen_text:`** String, default value is `'Password Incorrect!'`, text displayed if the wrong password is entered on the EaglercraftX client's password screen when joining the server with the built-in online mode authentication system.
- **`not_registered_screen_text:`** String, default value is `'You are not registered on this server!'`, text displayed when joining the server with the built-in online mode authentication system when using an account that has not been registered.
- **`eagler_command_name:`** String, default value is `'eagler'`, the name of the command to use for registering and/or logging in when joining the server with the built-in online mode authentication system.
- **`use_register_command_text:`** String, default value is `'&aUse /eagler to set an Eaglercraft password on this account'`, localization for when players use the `/eagler` command on the server.
- **`use_change_command_text:`** String, default value is `'&bUse /eagler to change your Eaglercraft password'`, localization for when players use the `/eagler` command on the server.
- **`command_success_text:`** String, default value is `'&bYour eagler password was changed successfully.'`, localization for when players use the `/eagler` command on the server.
- **`last_eagler_login_message:`** String, default value is `'Your last Eaglercraft login was on $date from $ip'`, localization for when players join the server with the built-in online mode authentication system.
- **`too_many_registrations_message:`** String, default value is `'&cThe maximum number of registrations has been reached for your IP address'`, localization for when players use the `/eagler` command on the server.
- **`need_vanilla_to_register_message:`** String, default value is `'&cYou need to log in with a vanilla account to use this command'`, localization for when players use the `/eagler` command on the server.
- **`override_eagler_to_vanilla_skins:`** Boolean, default value is `false`, if players who join the server after registering with an online mode account should show the same skin as the online-mode account they registered with.
- **`max_registration_per_ip:`** Integer, default value is `-1`, if greater than 0 it specifies the max number of accounts that can be created per IP address on the server with the built-in online mode authentication system.
### `ice_servers.yml`
The ice_servers.yml file is used for configuring the set of STUN/TURN servers that clients on this server should use for voice chat. Beware the default "openrelayproject" TURN servers are no longer active as of 2024, most likely as a result of being the default ond only TURN servers shipped with every copy of Eaglercraft to ever use WebRTC in some way.
- **`voice_servers_no_passwd:`** List of strings, defines a set of STUN/TURN server URIs to use that don't require a username and password.
- **`voice_servers_passwd:`** Section of sections, defines a set of STUN/TURN server URIs to use that do require a username and password, along with the username and password to use with each one.
### `updates.yml`
The updates.yml file is used for configuring the decentralized and totally legal update system used by EaglercraftX clients.
- **`block_all_client_updates:`** Boolean, default value is `false`, can be used to completely disable the update system.
- **`discard_login_packet_certs:`** Boolean, default value is `false`, can be used to prevent the server from relaying random crowdsourced update certificates that were recieved from players who joined the server using signed clients.
- **`cert_packet_data_rate_limit:`** Integer, default value is `524288`, can be used to set the global rate limit for how many bytes per second of certificates the server should send to all players.
- **`enable_eagcert_folder:`** Boolean, default value is `true`, can be used to enable or disable the "eagcert" folder used for distributing specific certificates as locally provided .cert files
- **`download_latest_certs:`** Boolean, default value is `true`, can be used to automaticlly download the latest certificates to the "eagcert" folder
- **`download_certs_from:`** List of strings, defines the URLs to download the certificates from if `download_latest_certs` is enabled
- **`check_for_update_every:`** Integer, default value is `28800` seconds, defines how often to check the URL list for updated certificates
### `pause_menu/pause_menu.yml`
For EaglercraftX u37 and up, can be used for changing the appearance of the pause menu and a few other screens with custom icons and strings, also used for enabling the "Server Info" webview and configuring its contents.
- **`enable_custom_pause_menu:`** Boolean, default value is `false`, if pause menu customization should be enabled on supported clients or not
- **`server_info_button:`** Section, defines properties of the "Server Info" button, which is always hidden unless pause menu customization is enabled
- **`enable_button:`** Boolean, default value is `true`, if the button should be shown or not
- **`button_text:`** String, default value is `'Server Info'`, the text to display on the button, useful if you want to use this feature for something other than a "Server Info" button
- **`button_mode_open_new_tab:`** Boolean, default value is `false`, can be used to make the "Server Info" button act as a hyperlink that opens a URL in a new tab instead of displaying content in an embedded webview iframe in the client.
- **`server_info_embed_url:`** String, default value is `''`, sets the URL for the "Server Info" button to use if it should open a URL in a new tab or the webview instead of directly downloading the markup to display from the EaglerXVelocity server itself over the WebSocket.
- **`button_mode_embed_file:`** Boolean, default value is `true`, determines if the "Server Info" button should download the webview markup directly from the EaglerXVelocity server over WebSocket instead of loading an external URL. Cannot be used with `button_mode_open_new_tab`!
- **`server_info_embed_file:`** String, default value is `'server_info.html'`, sets the name of the local file/template containing the markup to display in the "Server Info" webview if it is not in URL mode.
- **`server_info_embed_screen_title:`** String, default value is `'Server Info'`, sets the title string of the screen that displays the webview.
- **`server_info_embed_send_chunk_rate:`** Integer, default value is `1`, defines how many chunks of server info data to send per 250ms when downloading the server info markup to a client.
- **`server_info_embed_send_chunk_size:`** Integer, default value is `24576`, defines the size of each chunk of server info data when it is being downloaded to a client.
- **`enable_template_macros:`** Boolean, default value is `true`, if the server info markup should be processed for any eagler template macros (defined like `` {% arg1 `arg2` ... %} ``)
- **`server_info_embed_template_globals:`** Section, defines a list of additional global variables to use with the template processor
- **`allow_embed_template_eval_macro:`** Boolean, default value is `false`, if the template processor should allow the "eval" macro to be used in the server info markup file (not to be confused with the JavaScript function, although there is never a good reason to use JavaScript's eval function in your code either)
- **`enable_webview_javascript:`** Boolean, default value is `false`, if the server info webview should allow JavaScript to be executed or not. This will display an "allow JavaScript" screen to your players the first time they attempt to view it.
- **`enable_webview_message_api:`** Boolean, default value is `false`, if the server info webview has JavaScript enabled and should be permitted to open a message channel back to your EaglerXVelocity server to exchange arbitrary message packets. This can be used, for example, to implement a dynamic menu on your server using JavaScript and HTML that players can access through the server info webview that integrates directly with your gamemodes.
- **`enable_webview_strict_csp:`** Boolean, default value is `true`, if the `csp` attribute on the webview iframe should be set or not for added security, beware this is not supported on all browsers and will be silently disabled when the client detects it as unsupported.
- **`discord_button:`** Section, can be used to turn the "Invite" (formerly "Open to LAN") button on the pause menu into a "Discord" button that players can click to join your discord server
- **`enable_button:`** Boolean, default value is `true`, sets if the discord button should be enabled or not
- **`button_text:`** String, default value is `'Discord'`, sets the text that should be displayed on the button
- **`button_url:`** String, default value is `'https://invite url here'`, defines the URL to open when the button is pressed
- **`custom_images:`** Section, can be used to add icons to certain buttons, change the backgrounds of some screens, and add watermarks of your server to the inventory and pause menu and such if you're into that. For the best GPU compatibility, use only textures with powers of 2 as dimensions (such as 32x32 pixels), the width and height do not need to match, they just both need to be a power of 2. There is also a limit on the maximum size, icons can be no larger than 255x255 pixels (Effective max power-of-2 is 128x128). Color values will be downsampled to 16bpp and use a magic value to represent transparent pixels when the pause menu customization packet is sent to a client.
- **`icon_title_L:`** String, default value is `''`, sets the icon to show on the left side of the pause menu screen's title
- **`icon_title_R:`** String, default value is `''`, sets the icon to show on the right side of the pause menu screen's title
- **`icon_backToGame_L:`** String, default value is `''`, sets the icon to show on the left side of the "Back to Game" button
- **`icon_backToGame_R:`** String, default value is `''`, sets the icon to show on the right side of the "Back to Game" button
- **`icon_achievements_L:`** String, default value is `''`, sets the icon to show on the left side of the "Achievements" button
- **`icon_achievements_R:`** String, default value is `''`, sets the icon to show on the right side of the "Achievements" button
- **`icon_statistics_L:`** String, default value is `''`, sets the icon to show on the left side of the "Statistics" button
- **`icon_statistics_R:`** String, default value is `''`, sets the icon to show on the right side of the "Statistics" button
- **`icon_serverInfo_L:`** String, default value is `''`, sets the icon to show on the left side of the server info button
- **`icon_serverInfo_R:`** String, default value is `''`, sets the icon to show on the right side of the server info button
- **`icon_options_L:`** String, default value is `''`, sets the icon to show on the left side of the "Options" button
- **`icon_options_R:`** String, default value is `''`, sets the icon to show on the right side of the "Options" button
- **`icon_discord_L:`** String, default value is `''`, sets the icon to show on the left side of the discord button
- **`icon_discord_R:`** String, default value is `''`, sets the icon to show on the right side of the discord button
- **`icon_disconnect_L:`** String, default value is `''`, sets the icon to show on the left side of the "Disconnect" button
- **`icon_disconnect_R:`** String, default value is `''`, sets the icon to show on the right side of the "Disconnect" button
- **`icon_background_pause:`** String, default value is `'test_image.png'`, sets the icon to show as a repeating pattern in the background of the pause menu and related screens. It is especially important for GPU compatibility for this one to be a power-of-2 sized texture.
- **`icon_background_all:`** String, default value is `'test_image.png'`, sets the icon to show as a repeating pattern in the background of all other screens in the game. It is especially important for GPU compatibility for this one to be a power-of-2 sized texture.
- **`icon_watermark_pause:`** String, default value is `''`, sets a watermark to show in the bottom-left corner of the pause menu
- **`icon_watermark_all:`** String, default value is `''`, sets a watermark to show in the bottom-left corner of all other screens in the game
## Event Types
The events added by EaglerXVelocity are located in the [`net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.api.event`](gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/api/event) package and can be listened for the same way as you would for a regular Velocity event. When an EaglercraftX 1.8 player joins your server, all the regular Velocity login events are fired by EaglerXVelocity to maintain compatibility with other existing Velocity plugins, however EaglerXVelocity also adds several of its own event types to allow additional Eaglercraft specific features to be accessible through the main Velocity event bus as well.
- **`EaglercraftWebSocketOpenEvent`** Event that is fired when a new WebSocket connection is first opened to the server (regardless if its a query or actual player login attempt) useful for quickly filtering out new connections based on a specific origin or user agent header.
- **`EaglercraftClientBrandEvent`** Event that is fired when an EaglercraftX player joins, it contains the Eaglercraft client's "brand" and "version" strings along with the origin and user agent headers that can be used to detect 90% of the currently existing skid clients thanks to lax1dude making the base client self-snitch these commonly modified strings.
- **`EaglercraftIsAuthRequiredEvent`** Event that is fired when an EaglercraftX player attempts to join the server while the authentication system is enabled and `use_onboard_eaglerx_system` is `false`, used for implementing custom authentication systems.
- **`EaglercraftHandleAuthPasswordEvent`** Event that is fired when an EaglercraftX player enters a password into the "Authentication Required" screen while the authentication system is enabled and `use_onboard_eaglerx_system` is `false`, used for implementing custom authentication systems.
- **`EaglercraftHandleAuthCookieEvent`** Event that is fired when an EaglercraftX player joins the server with cookies set and while authentication system is enabled and `use_onboard_eaglerx_system` is `false`, you must set cookie auth as allowed while handling "EaglercraftIsAuthRequiredEvent" first for this event to actually be fired, used for implementing custom authentication systems that use cookies to store a session token for auto login.
- **`EaglercraftRevokeSessionQueryEvent`** Event that is fired when a player uses the "Revoke Session Token" feature in a u37 client to invalidate a cookie that was set on their client with the "revoke query supported" bit. Make sure to enable session revoke queries in listeners.yml!
- **`EaglercraftRegisterSkinEvent`** Event that is fired when an EaglercraftX player's skin is recieved, can be used to analyze or modify or replace the skin with a different texture or preset ID if needed. Note that players using pre-u37 clients may not see the modified/replaced skin.
- **`EaglercraftRegisterCapeEvent`** Event that is fired when an EaglercraftX player's cape is recieved, can be used to analyze or modify or replace the cape with a different texture or preset ID if needed. Use preset ID 0 to disable their cape entirely. Note that players using pre-u37 clients may not see the modified/replaced cape.
- **`EaglercraftMOTDEvent`** Event that is fired when a MOTD query request is recieved, used for implement a custom server MOTD handler, or implementing an animated MOTD like the EaglerMOTD plugin.
- **`EaglercraftVoiceStatusChangeEvent`** Event that is fired when `allow_voice` is enabled and a player transitions between voice states (SERVER_DISABLE, DISABLED, ENABLED) cannot be cancelled so it is mostly just useful for logging or displaying some "Rules" in chat.
- **`EaglercraftWebViewChannelEvent`** Event that is fired when the server info webview is open and JavaScript is enabled and the webview opens/closes a new message channel to EaglerXVelocity.
- **`EaglercraftWebViewMessageEvent`** Event that is fired when the server info webview is open and JavaScript is enabled and the webview has already opened a channel to EaglerXVelocity and a new message is recieved on that open channel.
## Registering Queries
If you would like to add your own custom `Accept:` query handlers to the proxy (along with MOTD, version, and session revoke) you can register them at startup using the register functions provided by the [`EaglerQueryHandler`](gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/api/query/EaglerQueryHandler.java) class.
## EaglerXVelocityAPIHelper
To help make plugin development easier, a class called [`EaglerXVelocityAPIHelper`](gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/api/EaglerXVelocityAPIHelper.java) is included that defines dozens of helper functions for easily and safely interacting with EaglercraftX clients. This is to enable developers to program plugins for EaglerXVelocity servers with minimal knowledge of the actual underlying protocol used by the client for skins and capes and voice and other exclusive features. Its recommended to convert all your existing code to use the `EaglerXVelocityAPIHelper` instead of whatever packet hacks you were doing before it was added when you migrate your network to EaglerXVelocity 1.1.0+. All your existing packet hacks will be broken anyway unless you configure the server to force all u37+ clients to use protocol V3 instead.

View File

@ -1,10 +0,0 @@
lib/*
.idea/*
*.iml
out/*
/.gradle/
/.settings/
.classpath
.project
/build/
/bin/

View File

@ -1,41 +0,0 @@
plugins {
id 'java'
id 'eclipse'
}
group = 'net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper'
version = ''
repositories {
mavenCentral()
maven {
url = 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/'
}
maven {
url = "https://oss.sonatype.org/content/repositories/snapshots/"
}
}
sourceSets {
main {
java {
srcDirs 'src/main/java'
srcDirs '../backend-rpc-protocol/src/backend-rpc-protocol/java'
srcDirs '../backend-rpc-protocol/src/notif-builder-bungee/java'
}
}
}
dependencies {
compileOnly 'org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
jar {
compileJava.options.encoding = 'UTF-8'
javadoc.options.encoding = 'UTF-8'
}

View File

@ -1,6 +0,0 @@
#Thu Jun 20 10:14:47 CDT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,234 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -1,2 +0,0 @@
rootProject.name = 'EaglercraftXBukkitAPI'

View File

@ -1,71 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.messaging.PluginMessageListener;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl.PlayerDataObj;
public class EaglerXBukkitAPIListener implements Listener, PluginMessageListener {
@EventHandler
public void onLoginEvent(PlayerLoginEvent evt) {
PlayerDataObj.setupPlayer(evt.getPlayer());
}
@Override
public void onPluginMessageReceived(String channel, Player player, byte[] data) {
PlayerDataObj dataObj;
switch(channel) {
case EaglerBackendRPCProtocol.CHANNEL_NAME:
case EaglerBackendRPCProtocol.CHANNEL_NAME_MODERN:
dataObj = PlayerDataObj.getForPlayer(player);
if(dataObj != null) {
dataObj.firePluginMsgRecievedInternal(data);
}
break;
case EaglerBackendRPCProtocol.CHANNEL_NAME_READY:
dataObj = PlayerDataObj.getForPlayer(player);
if(dataObj != null) {
dataObj.firePluginReadyMsgRecieved(false);
}
break;
case EaglerBackendRPCProtocol.CHANNEL_NAME_READY_MODERN:
dataObj = PlayerDataObj.getForPlayer(player);
if(dataObj != null) {
dataObj.firePluginReadyMsgRecieved(true);
}
default:
break;
}
}
@EventHandler
public void onQuitEvent(PlayerQuitEvent evt) {
PlayerDataObj dataObj = PlayerDataObj.getForPlayer(evt.getPlayer());
if(dataObj != null) {
dataObj.firePlayerQuitEventInternal();
}
}
}

View File

@ -1,120 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper;
import java.util.Collection;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Logger;
import org.bukkit.Server;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.plugin.messaging.Messenger;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl.PlayerDataObj;
public class EaglerXBukkitAPIPlugin extends JavaPlugin {
private static EaglerXBukkitAPIPlugin instance = null;
private Timer timeoutHandler;
public EaglerXBukkitAPIPlugin() {
instance = this;
}
@Override
public void onLoad() {
}
@Override
public void onEnable() {
EaglerXBukkitAPIListener ls = new EaglerXBukkitAPIListener();
Server svr = getServer();
svr.getPluginManager().registerEvents(ls, this);
Messenger msgr = svr.getMessenger();
boolean registerLegacy = !isPost_v1_13();
if(registerLegacy) {
try {
msgr.registerOutgoingPluginChannel(this, EaglerBackendRPCProtocol.CHANNEL_NAME);
}catch(Throwable t) {
registerLegacy = false;
}
}
if(!registerLegacy) {
getLogger().warning("Note: Only the modernized plugin channel names can be used for this server!");
getLogger().warning("Make sure to set \"use_modernized_channel_names: true\" in bungee/velocity plugin settings.yml");
}
msgr.registerOutgoingPluginChannel(this, EaglerBackendRPCProtocol.CHANNEL_NAME_MODERN);
if(registerLegacy) {
msgr.registerIncomingPluginChannel(this, EaglerBackendRPCProtocol.CHANNEL_NAME, ls);
}
msgr.registerIncomingPluginChannel(this, EaglerBackendRPCProtocol.CHANNEL_NAME_MODERN, ls);
if(registerLegacy) {
msgr.registerIncomingPluginChannel(this, EaglerBackendRPCProtocol.CHANNEL_NAME_READY, ls);
}
msgr.registerIncomingPluginChannel(this, EaglerBackendRPCProtocol.CHANNEL_NAME_READY_MODERN, ls);
if(timeoutHandler == null) {
timeoutHandler = new Timer("EaglerXBukkitAPI: Timeout cleanup thread");
timeoutHandler.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
Collection<? extends Player> pp = EaglerXBukkitAPIPlugin.this.getServer().getOnlinePlayers();
if(!pp.isEmpty()) {
long now = System.nanoTime() / 1000000l;
for(Player p : pp) {
PlayerDataObj.getForPlayer(p).fireCheckRequestTimeoutsInternal(now);
}
}
}
}, 0l, 5000l);
}
}
@Override
public void onDisable() {
getServer().getMessenger().unregisterOutgoingPluginChannel(this);
getServer().getMessenger().unregisterIncomingPluginChannel(this);
if(timeoutHandler != null) {
timeoutHandler.cancel();
timeoutHandler = null;
}
}
public static EaglerXBukkitAPIPlugin getEagler() {
return instance;
}
public static Logger logger() {
return instance.getLogger();
}
private boolean isPost_v1_13() {
String[] ver = getServer().getVersion().split("[\\.\\-]");
if(ver.length >= 2) {
try {
return Integer.parseInt(ver[0]) >= 1 || Integer.parseInt(ver[1]) >= 13;
}catch(NumberFormatException ex) {
}
}
return false;
}
}

View File

@ -1,38 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
public class ClientBrandUUIDs {
public static final UUID BRAND_NULL_UUID = new UUID(0l, 0l);
public static final UUID BRAND_VANILLA_UUID = new UUID(0x1DCE015CD384374El, 0x85030A4DE95E5736l);
public static final UUID BRAND_EAGLERCRAFTX_V4_UUID = makeClientBrandUUID("EaglercraftX");
public static final UUID BRAND_EAGLERCRAFTX_LEGACY_UUID = makeClientBrandUUIDLegacy("EaglercraftX");
public static UUID makeClientBrandUUID(String name) {
return UUID.nameUUIDFromBytes(("EaglercraftXClient:" + name).getBytes(StandardCharsets.UTF_8));
}
public static UUID makeClientBrandUUIDLegacy(String name) {
return UUID.nameUUIDFromBytes(("EaglercraftXClientOld:" + name).getBytes(StandardCharsets.UTF_8));
}
}

View File

@ -1,36 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api;
public class EaglerRPCException extends RuntimeException {
public EaglerRPCException() {
}
public EaglerRPCException(String message, Throwable cause) {
super(message, cause);
}
public EaglerRPCException(String message) {
super(message);
}
public EaglerRPCException(Throwable cause) {
super(cause);
}
}

View File

@ -1,46 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api;
public class EaglerRPCInitException extends EaglerRPCException {
private int code;
public EaglerRPCInitException(int code) {
this.code = code;
}
public EaglerRPCInitException(int code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
public EaglerRPCInitException(int code, String message) {
super(message);
this.code = code;
}
public EaglerRPCInitException(int code, Throwable cause) {
super(cause);
this.code = code;
}
public int getFailureCodeEagler() {
return code;
}
}

View File

@ -1,40 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCSubscribeEvents;
public enum EnumSubscribeEvents {
/** @see net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.EventWebViewOpenClose */
EVENT_WEBVIEW_OPEN_CLOSE(CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_OPEN_CLOSE),
/** @see net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.EventWebViewMessage */
EVENT_WEBVIEW_MESSAGE(CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_WEBVIEW_MESSAGE),
/** @see net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.EventToggledVoice */
EVENT_TOGGLE_VOICE(CPacketRPCSubscribeEvents.SUBSCRIBE_EVENT_TOGGLE_VOICE);
protected static final EnumSubscribeEvents[] _VALUES = values();
public final int bit;
private EnumSubscribeEvents(int bit) {
this.bit = bit;
}
}

View File

@ -1,23 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api;
public interface IEaglerRPCCloseHandler {
void handleEvent(IEaglerXBukkitAPI api);
}

View File

@ -1,25 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.IEaglerRPCEvent;
public interface IEaglerRPCEventHandler<T extends IEaglerRPCEvent> {
void handleEvent(IEaglerXBukkitAPI api, EnumSubscribeEvents event, T data);
}

View File

@ -1,48 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api;
import java.util.concurrent.Executor;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl.SameThreadExecutor;
public interface IEaglerRPCFuture<V> extends ListenableFuture<V> {
/**
* Warning: Futures.addCallback is recommended!
*/
default void addListener(Runnable runnable) {
addListener(runnable, SameThreadExecutor.SAME_THREAD_EXECUTOR);
}
default void addCallback(FutureCallback<V> runnable, Executor executor) {
Futures.addCallback(this, runnable, executor);
}
default void addCallback(FutureCallback<V> runnable) {
Futures.addCallback(this, runnable, SameThreadExecutor.SAME_THREAD_EXECUTOR);
}
void setExpiresMSFromNow(int millis);
boolean hasExpired();
}

View File

@ -1,244 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import org.bukkit.entity.Player;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCPacket;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCNotifBadgeShow;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCSetPauseMenuCustom;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.NotificationBadgeBuilder;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.PacketImageData;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.SkinPacketHelper;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.IEaglerRPCEvent;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseBytes;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseCookie;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseString;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseUUID;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseVoiceStatus;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.ResponseWebViewStatus;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl.EaglerXBukkitImpl;
public interface IEaglerXBukkitAPI {
public static IEaglerRPCFuture<IEaglerXBukkitAPI> getAPI(Player player) {
IEaglerRPCFuture<IEaglerXBukkitAPI> futureRet = EaglerXBukkitImpl.getAPI(player);
futureRet.setExpiresMSFromNow(EaglerXBukkitImpl.DEFAULT_TIMEOUT);
return futureRet;
}
public static IEaglerRPCFuture<IEaglerXBukkitAPI> getAPI(Player player, int timeoutMS) {
IEaglerRPCFuture<IEaglerXBukkitAPI> futureRet = EaglerXBukkitImpl.getAPI(player);
futureRet.setExpiresMSFromNow(timeoutMS);
return futureRet;
}
EaglerBackendRPCProtocol getRPCProtocolVersion();
int getEaglerProtocolVersion();
boolean isOpen();
void closeAPI();
Player getPlayer();
void sendRPCPacket(EaglerBackendRPCPacket packet);
default void subscribeEvents(EnumSubscribeEvents...events) {
int bits = 0;
for(int i = 0; i < events.length; ++i) {
bits |= events[i].bit;
}
subscribeEvents(bits);
}
void subscribeEvents(int events);
default void unsubscribeEvents(EnumSubscribeEvents...events) {
int bits = 0;
for(int i = 0; i < events.length; ++i) {
bits |= events[i].bit;
}
unsubscribeEvents(bits);
}
void unsubscribeEvents(int events);
void unsubscribeAllEvents();
int getSubscribedEventsBits();
default Set<EnumSubscribeEvents> getSubscribedEvents() {
Set<EnumSubscribeEvents> ret = new HashSet<>(4);
int bits = getSubscribedEventsBits();
EnumSubscribeEvents[] enums = EnumSubscribeEvents._VALUES;
for(int i = 0; i < enums.length; ++i) {
if((bits & enums[i].bit) != 0) {
ret.add(enums[i]);
}
}
return ret;
}
void addEventListener(EnumSubscribeEvents eventType, IEaglerRPCEventHandler<? extends IEaglerRPCEvent> handler);
void removeEventListener(EnumSubscribeEvents eventType, IEaglerRPCEventHandler<? extends IEaglerRPCEvent> handler);
void removeEventListeners(EnumSubscribeEvents eventType);
void addCloseListener(IEaglerRPCCloseHandler handler);
void removeCloseListener(IEaglerRPCCloseHandler handler);
void removeCloseListeners();
boolean redirectPlayerSupported();
void redirectPlayerToWebSocket(String webSocketURI);
void setBaseRequestTimeout(int seconds);
IEaglerRPCFuture<ResponseUUID> requestPlayerProfileUUID();
IEaglerRPCFuture<ResponseString> requestPlayerRealIP();
IEaglerRPCFuture<ResponseString> requestPlayerOrigin();
IEaglerRPCFuture<ResponseString> requestPlayerUserAgent();
IEaglerRPCFuture<ResponseBytes> requestPlayerSkinData();
IEaglerRPCFuture<ResponseBytes> requestPlayerCapeData();
IEaglerRPCFuture<ResponseCookie> requestPlayerCookieData();
IEaglerRPCFuture<ResponseString> requestPlayerClientBrandStr();
IEaglerRPCFuture<ResponseString> requestPlayerClientVersionStr();
IEaglerRPCFuture<ResponseString> requestPlayerClientBrandAndVersionStr();
IEaglerRPCFuture<ResponseUUID> requestPlayerClientBrandUUID();
IEaglerRPCFuture<ResponseVoiceStatus> requestPlayerVoiceStatus();
IEaglerRPCFuture<ResponseWebViewStatus> requestPlayerWebViewStatus();
void sendRawCustomPayloadPacket(String channel, byte[] data);
default void sendRawEaglerPacketV4(byte[] data) {
sendRawCustomPayloadPacket("EAG|1.8", data);
}
boolean pauseMenuCustomizationSupported();
void setPauseMenuCustomizationState(CPacketRPCSetPauseMenuCustom packet);
void sendWebViewMessageString(String channelName, String data);
void sendWebViewMessageBytes(String channelName, byte[] data);
void forcePlayerSkin(byte[] skinData, boolean notifyOthers);
default void forcePlayerSkinPreset(int presetID, boolean notifyOthers) {
forcePlayerSkin(SkinPacketHelper.writePresetSkinPacket(presetID), notifyOthers);
}
default void forcePlayerSkinCustom(int modelId, byte[] texture64x64, boolean notifyOthers) {
forcePlayerSkin(SkinPacketHelper.writeCustomSkinPacket(modelId, texture64x64), notifyOthers);
}
void forcePlayerCape(byte[] capeData, boolean notifyOthers);
default void forcePlayerCapePreset(int presetID, boolean notifyOthers) {
forcePlayerCape(SkinPacketHelper.writePresetCapePacket(presetID), notifyOthers);
}
default void forcePlayerCapeCustom(byte[] texture32x32, boolean notifyOthers) {
forcePlayerCape(SkinPacketHelper.writeCustomCapePacket(texture32x32), notifyOthers);
}
void setCookieData(byte[] cookieData, int expiresAfterSec, boolean revokeQuerySupported, boolean saveToDisk);
default void setCookieData(byte[] cookieData, int expiresAfter, TimeUnit expiresTimeUnit, boolean revokeQuerySupported, boolean saveToDisk) {
setCookieData(cookieData, (int)expiresTimeUnit.toSeconds(expiresAfter), revokeQuerySupported, saveToDisk);
}
default void setCookieData(byte[] cookieData, int expiresAfterSec, boolean revokeQuerySupported) {
setCookieData(cookieData, expiresAfterSec, revokeQuerySupported, true);
}
default void setCookieData(byte[] cookieData, int expiresAfter, TimeUnit expiresTimeUnit, boolean revokeQuerySupported) {
setCookieData(cookieData, (int)expiresTimeUnit.toSeconds(expiresAfter), revokeQuerySupported, true);
}
default void setCookieData(byte[] cookieData, int expiresAfterSec) {
setCookieData(cookieData, expiresAfterSec, false, true);
}
default void setCookieData(byte[] cookieData, int expiresAfter, TimeUnit expiresTimeUnit) {
setCookieData(cookieData, (int)expiresTimeUnit.toSeconds(expiresAfter), false, true);
}
default void clearCookieData() {
setCookieData(null, 0, false, false);
}
void setFNAWSkinsEnabled(boolean enabled, boolean force);
void setFNAWSkinsEnabled(boolean enabled);
void resetForcedMulti(boolean resetSkin, boolean resetCape, boolean resetFNAWForce, boolean notifyOtherPlayers);
void resetForcedSkin(boolean notifyOtherPlayers);
void resetForcedCape(boolean notifyOtherPlayers);
void resetForcedFNAW();
boolean notifSupported();
void notifIconRegister(Map<UUID, PacketImageData> iconsToRegister);
void notifIconRegister(UUID iconUUID, PacketImageData imageData);
void notifIconRelease(Collection<UUID> iconsToRelease);
void notifIconRelease(UUID iconUUID);
void notifBadgeShow(CPacketRPCNotifBadgeShow packet);
default void notifBadgeShow(NotificationBadgeBuilder packet) {
notifBadgeShow(packet.buildPacket());
}
void notifBadgeHide(UUID badgeUUID);
<T> void setMeta(String key, T value);
<T> T getMeta(String key);
}

View File

@ -1,97 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import javax.imageio.ImageIO;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.PacketImageData;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.SkinPacketHelper;
public class ImageDataLoader {
public static PacketImageData loadPacketImageData(File img) throws IOException {
return loadPacketImageData(ImageIO.read(img), 255, 255);
}
public static PacketImageData loadPacketImageData(File img, int maxWidth, int maxHeight) throws IOException {
return loadPacketImageData(ImageIO.read(img), maxWidth, maxHeight);
}
public static PacketImageData loadPacketImageData(BufferedImage img) {
return loadPacketImageData(img, 255, 255);
}
public static PacketImageData loadPacketImageData(BufferedImage img, int maxWidth, int maxHeight) {
int w = img.getWidth();
int h = img.getHeight();
if(w > maxWidth || h > maxHeight) {
float aspectRatio = (float)w / (float)h;
int nw, nh;
if(aspectRatio >= 1.0f) {
nw = (int)(maxWidth / aspectRatio);
nh = maxHeight;
}else {
nw = maxWidth;
nh = (int)(maxHeight * aspectRatio);
}
BufferedImage resized = new BufferedImage(nw, nh, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) resized.getGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setBackground(new Color(0, true));
g.clearRect(0, 0, nw, nh);
g.drawImage(img, 0, 0, nw, nh, 0, 0, w, h, null);
g.dispose();
img = resized;
}
int[] pixels = new int[w * h];
img.getRGB(0, 0, w, h, pixels, 0, w);
return new PacketImageData(w, h, pixels);
}
public static byte[] loadCustomSkin(File texture64x64) throws IOException {
return SkinPacketHelper.loadCustomSkin(texture64x64);
}
public static byte[] loadCustomSkin(InputStream texture64x64) throws IOException {
return SkinPacketHelper.loadCustomSkin(texture64x64);
}
public static byte[] loadCustomSkin(BufferedImage texture64x64) {
return SkinPacketHelper.loadCustomSkin(texture64x64);
}
public static byte[] loadCustomCape(File textureNx64) throws IOException {
return SkinPacketHelper.loadCustomCape(textureNx64);
}
public static byte[] loadCustomCape(InputStream textureNx64) throws IOException {
return SkinPacketHelper.loadCustomCape(textureNx64);
}
public static byte[] loadCustomCape(BufferedImage textureNx64) {
return SkinPacketHelper.loadCustomCape(textureNx64);
}
}

View File

@ -1,40 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents;
public class EventToggledVoice implements IEaglerRPCEvent {
public static enum VoiceState {
SERVER_DISABLE, DISABLED, ENABLED;
}
public final VoiceState oldVoiceState;
public final VoiceState newVoiceState;
public EventToggledVoice(VoiceState oldVoiceState, VoiceState newVoiceState) {
this.oldVoiceState = oldVoiceState;
this.newVoiceState = newVoiceState;
}
@Override
public EnumSubscribeEvents getType() {
return EnumSubscribeEvents.EVENT_TOGGLE_VOICE;
}
}

View File

@ -1,60 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event;
import java.nio.charset.StandardCharsets;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents;
public class EventWebViewMessage implements IEaglerRPCEvent {
public static enum MessageType {
STRING, BINARY;
}
public final String channelName;
public final MessageType messageType;
protected final byte[] messageContent;
protected String asString = null;
public EventWebViewMessage(String channelName, MessageType messageType, byte[] messageContent) {
this.channelName = channelName;
this.messageType = messageType;
this.messageContent = messageContent;
}
public String getContentStr() {
if(messageType == MessageType.STRING) {
if(asString == null) {
asString = new String(messageContent, StandardCharsets.UTF_8);
}
return asString;
}else {
return null;
}
}
public byte[] getContentBytes() {
return messageType == MessageType.BINARY ? messageContent : null;
}
@Override
public EnumSubscribeEvents getType() {
return EnumSubscribeEvents.EVENT_WEBVIEW_MESSAGE;
}
}

View File

@ -1,36 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents;
public class EventWebViewOpenClose implements IEaglerRPCEvent {
public final String channelName;
public final boolean opened;
public EventWebViewOpenClose(String channelName, boolean opened) {
this.channelName = channelName;
this.opened = opened;
}
@Override
public EnumSubscribeEvents getType() {
return EnumSubscribeEvents.EVENT_WEBVIEW_OPEN_CLOSE;
}
}

View File

@ -1,25 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents;
public interface IEaglerRPCEvent {
EnumSubscribeEvents getType();
}

View File

@ -1,38 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCException;
public class EaglerRPCResponseException extends EaglerRPCException {
public EaglerRPCResponseException() {
}
public EaglerRPCResponseException(String message, Throwable cause) {
super(message, cause);
}
public EaglerRPCResponseException(String message) {
super(message);
}
public EaglerRPCResponseException(Throwable cause) {
super(cause);
}
}

View File

@ -1,30 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCException;
public class EaglerRPCTimeoutException extends EaglerRPCException {
public EaglerRPCTimeoutException() {
}
public EaglerRPCTimeoutException(String message) {
super(message);
}
}

View File

@ -1,27 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
public interface IEaglerRPCResponse {
IEaglerXBukkitAPI getSource();
int getRequestID();
}

View File

@ -1,43 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
public class ResponseBytes implements IEaglerRPCResponse {
protected final IEaglerXBukkitAPI source;
protected final int requestID;
public final byte[] bytes;
public ResponseBytes(IEaglerXBukkitAPI source, int requestID, byte[] bytes) {
this.source = source;
this.requestID = requestID;
this.bytes = bytes;
}
@Override
public IEaglerXBukkitAPI getSource() {
return source;
}
@Override
public int getRequestID() {
return requestID;
}
}

View File

@ -1,45 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
public class ResponseCookie implements IEaglerRPCResponse {
protected final IEaglerXBukkitAPI source;
protected final int requestID;
public final boolean cookieEnabled;
public final byte[] cookieBytes;
public ResponseCookie(IEaglerXBukkitAPI source, int requestID, boolean cookieEnabled, byte[] cookieBytes) {
this.source = source;
this.requestID = requestID;
this.cookieEnabled = cookieEnabled;
this.cookieBytes = cookieBytes;
}
@Override
public IEaglerXBukkitAPI getSource() {
return source;
}
@Override
public int getRequestID() {
return requestID;
}
}

View File

@ -1,43 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
public class ResponseString implements IEaglerRPCResponse {
protected final IEaglerXBukkitAPI source;
protected final int requestID;
public final String string;
public ResponseString(IEaglerXBukkitAPI source, int requestID, String string) {
this.source = source;
this.requestID = requestID;
this.string = string;
}
@Override
public IEaglerXBukkitAPI getSource() {
return source;
}
@Override
public int getRequestID() {
return requestID;
}
}

View File

@ -1,45 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response;
import java.util.UUID;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
public class ResponseUUID implements IEaglerRPCResponse {
protected final IEaglerXBukkitAPI source;
protected final int requestID;
public final UUID uuid;
public ResponseUUID(IEaglerXBukkitAPI source, int requestID, UUID uuid) {
this.source = source;
this.requestID = requestID;
this.uuid = uuid;
}
@Override
public IEaglerXBukkitAPI getSource() {
return source;
}
@Override
public int getRequestID() {
return requestID;
}
}

View File

@ -1,47 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
public class ResponseVoiceStatus implements IEaglerRPCResponse {
public static enum VoiceState {
SERVER_DISABLE, DISABLED, ENABLED;
}
protected final IEaglerXBukkitAPI source;
protected final int requestID;
public final VoiceState voiceState;
public ResponseVoiceStatus(IEaglerXBukkitAPI source, int requestID, VoiceState voiceState) {
this.source = source;
this.requestID = requestID;
this.voiceState = voiceState;
}
@Override
public IEaglerXBukkitAPI getSource() {
return source;
}
@Override
public int getRequestID() {
return requestID;
}
}

View File

@ -1,49 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
public class ResponseWebViewStatus implements IEaglerRPCResponse {
public static enum WebViewState {
NOT_SUPPORTED, SERVER_DISABLE, CHANNEL_CLOSED, CHANNEL_OPEN;
}
protected final IEaglerXBukkitAPI source;
protected final int requestID;
public final WebViewState webviewState;
public final String channelName;
public ResponseWebViewStatus(IEaglerXBukkitAPI source, int requestID, WebViewState webviewState, String channelName) {
this.source = source;
this.requestID = requestID;
this.webviewState = webviewState;
this.channelName = channelName;
}
@Override
public IEaglerXBukkitAPI getSource() {
return source;
}
@Override
public int getRequestID() {
return requestID;
}
}

View File

@ -1,56 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl;
import com.google.common.util.concurrent.AbstractFuture;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerRPCFuture;
public class EaglerRPCFutureImpl<V> extends AbstractFuture<V> implements IEaglerRPCFuture<V> {
private volatile long timeStart = -1l;
private volatile int timeoutAfter = -1;
@Override
public void setExpiresMSFromNow(int millis) {
if(millis > 0) {
timeStart = System.nanoTime() / 1000000l;
timeoutAfter = millis;
}else {
timeStart = -1l;
timeoutAfter = -1;
}
}
@Override
public boolean hasExpired() {
return (timeStart > 0l && timeoutAfter > 0) ? ((System.nanoTime() / 1000000l) - timeStart) > timeoutAfter : false;
}
public boolean hasExpiredBetter(long now) {
return (timeStart > 0l && timeoutAfter > 0) ? (now - timeStart) > timeoutAfter : false;
}
public void fireCompleteInternal(V value) {
this.set(value);
}
public void fireExceptionInternal(Throwable value) {
this.setException(value);
}
}

View File

@ -1,789 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import org.bukkit.entity.Player;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder.ListMultimapBuilder;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCHandler;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCPacket;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.*;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.*;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.util.PacketImageData;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.EaglerXBukkitAPIPlugin;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCException;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EnumSubscribeEvents;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerRPCCloseHandler;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerRPCEventHandler;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerRPCFuture;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.event.*;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.*;
public class EaglerXBukkitImpl implements IEaglerXBukkitAPI {
public static final int DEFAULT_TIMEOUT = 10000;
public static EaglerRPCFutureImpl<IEaglerXBukkitAPI> getAPI(Player player) {
if(player == null) {
throw new NullPointerException("Player cannot be null!");
}
PlayerDataObj data = PlayerDataObj.getForPlayer(player);
if(data == null) {
throw new IllegalStateException("Player object is not ready yet for EaglerXBukkitAPI.createAPI()! (Try hooking PlayerJoinEvent instead of PlayerLoginEvent you hooker)");
}
boolean sendHello;
synchronized(data) {
if(data.openFuture != null) {
return data.openFuture;
}
if(data.currentAPI != null) {
EaglerRPCFutureImpl<IEaglerXBukkitAPI> completeImmedately = new EaglerRPCFutureImpl();
completeImmedately.fireCompleteInternal(data.currentAPI);
return completeImmedately;
}
data.openFuture = new EaglerRPCFutureImpl();
sendHello = data.hasRecievedReady;
}
if(sendHello) {
sendHelloPacket(data.pluginChName, player);
}
return data.openFuture;
}
protected static void sendHelloPacket(String channel, Player player) {
player.sendPluginMessage(EaglerXBukkitAPIPlugin.getEagler(), channel, HelloPacketFactory.BASE_HELLO_PACKET);
}
protected static EaglerXBukkitImpl createFromHandshakeInternal(PlayerDataObj playerDataObj, SPacketRPCEnabledSuccess pkt) {
return new EaglerXBukkitImpl(playerDataObj, EaglerBackendRPCProtocol.getByID(pkt.selectedRPCProtocol), pkt.playerClientProtocol);
}
protected final PlayerDataObj playerDataObj;
protected final Player playerObj;
protected final EaglerBackendRPCProtocol protocol;
protected final int gameProtocol;
protected boolean open;
protected final Map<String,Object> metadata = new ConcurrentHashMap<>(4);
protected final EaglerXBukkitAPIHandler packetHandler;
protected int subscribedEvents = 0;
protected final ListMultimap<EnumSubscribeEvents, IEaglerRPCEventHandler<? extends IEaglerRPCEvent>> eventHandlers = ListMultimapBuilder
.hashKeys().arrayListValues().build();
protected final List<IEaglerRPCCloseHandler> closeHandlers = new ArrayList<>(4);
protected final Map<Integer,EaglerRPCFutureImpl<? extends IEaglerRPCResponse>> waitingRequests = new ConcurrentHashMap<>();
protected int baseTimeout = DEFAULT_TIMEOUT;
protected final AtomicInteger requestIDGenerator = new AtomicInteger();
private final ReentrantLock inputStreamLock = new ReentrantLock();
private final ReentrantLock outputStreamLock = new ReentrantLock();
private final ReusableByteArrayInputStream reusableInputStream = new ReusableByteArrayInputStream();
private final ReusableByteArrayOutputStream reusableOutputStream = new ReusableByteArrayOutputStream();
private final DataInputStream dataInputStream = new DataInputStream(reusableInputStream);
private final DataOutputStream dataOutputStream = new DataOutputStream(reusableOutputStream);
protected EaglerXBukkitImpl(PlayerDataObj playerDataObj, EaglerBackendRPCProtocol protocol, int gameProtocol) {
this.playerDataObj = playerDataObj;
this.playerObj = playerDataObj.player;
this.protocol = protocol;
this.gameProtocol = gameProtocol;
this.open = true;
this.packetHandler = new EaglerXBukkitAPIHandler();
}
@Override
public EaglerBackendRPCProtocol getRPCProtocolVersion() {
return protocol;
}
@Override
public int getEaglerProtocolVersion() {
return gameProtocol;
}
@Override
public boolean isOpen() {
return open;
}
@Override
public void closeAPI() {
if(open) {
try {
sendRPCPacket(new CPacketRPCDisabled());
}finally {
fireAPIClosedEventInternal();
}
}
}
protected void fireAPIClosedEventInternal() {
if(!open) return;
open = false;
synchronized(closeHandlers) {
for(int i = 0, l = closeHandlers.size(); i < l; ++i) {
IEaglerRPCCloseHandler hd = closeHandlers.get(i);
try {
hd.handleEvent(this);
}catch(Throwable t) {
EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE,
"[" + playerObj.getName() + "] caught exception while firing close handler " + hd, t);
}
}
}
}
protected void fireAPIPacketRecievedInternal(EaglerBackendRPCPacket ret) {
ret.handlePacket(packetHandler);
}
protected <T extends IEaglerRPCEvent> void fireEventHandlers(T eventObj) {
EnumSubscribeEvents type = eventObj.getType();
List<IEaglerRPCEventHandler<? extends IEaglerRPCEvent>> lst;
synchronized(eventHandlers) {
lst = eventHandlers.get(type);
}
for(int i = 0, l = lst.size(); i < l; ++i) {
IEaglerRPCEventHandler<T> handler = (IEaglerRPCEventHandler<T>) lst.get(i);
try {
handler.handleEvent(this, type, eventObj);
}catch(Throwable t) {
EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE,
"[" + playerObj.getName() + "] caught exception while processing event type "
+ type + " using handler " + handler, t);
}
}
}
protected class EaglerXBukkitAPIHandler implements EaglerBackendRPCHandler {
public void handleServer(SPacketRPCResponseTypeNull packet) {
EaglerRPCFutureImpl<? extends IEaglerRPCResponse> future = waitingRequests.remove(packet.requestID);
if(future != null) {
future.fireCompleteInternal(null);
}
}
public void handleServer(SPacketRPCResponseTypeBytes packet) {
EaglerRPCFutureImpl<ResponseBytes> future = (EaglerRPCFutureImpl<ResponseBytes>)waitingRequests.remove(packet.requestID);
if(future != null) {
future.fireCompleteInternal(new ResponseBytes(EaglerXBukkitImpl.this, packet.requestID, packet.response));
}
}
public void handleServer(SPacketRPCResponseTypeString packet) {
EaglerRPCFutureImpl<ResponseString> future = (EaglerRPCFutureImpl<ResponseString>)waitingRequests.remove(packet.requestID);
if(future != null) {
future.fireCompleteInternal(new ResponseString(EaglerXBukkitImpl.this, packet.requestID, packet.response));
}
}
public void handleServer(SPacketRPCResponseTypeUUID packet) {
EaglerRPCFutureImpl<ResponseUUID> future = (EaglerRPCFutureImpl<ResponseUUID>)waitingRequests.remove(packet.requestID);
if(future != null) {
future.fireCompleteInternal(new ResponseUUID(EaglerXBukkitImpl.this, packet.requestID, packet.uuid));
}
}
public void handleServer(SPacketRPCResponseTypeCookie packet) {
EaglerRPCFutureImpl<ResponseCookie> future = (EaglerRPCFutureImpl<ResponseCookie>)waitingRequests.remove(packet.requestID);
if(future != null) {
future.fireCompleteInternal(new ResponseCookie(EaglerXBukkitImpl.this, packet.requestID, packet.cookiesEnabled, packet.cookieData));
}
}
public void handleServer(SPacketRPCResponseTypeVoiceStatus packet) {
EaglerRPCFutureImpl<ResponseVoiceStatus> future = (EaglerRPCFutureImpl<ResponseVoiceStatus>)waitingRequests.remove(packet.requestID);
if(future != null) {
future.fireCompleteInternal(new ResponseVoiceStatus(EaglerXBukkitImpl.this, packet.requestID, translateVoiceState2(packet.voiceState)));
}
}
public void handleServer(SPacketRPCResponseTypeWebViewStatus packet) {
EaglerRPCFutureImpl<ResponseWebViewStatus> future = (EaglerRPCFutureImpl<ResponseWebViewStatus>)waitingRequests.remove(packet.requestID);
if(future != null) {
future.fireCompleteInternal(new ResponseWebViewStatus(EaglerXBukkitImpl.this, packet.requestID, translateWebViewState(packet.webviewState), packet.channelName));
}
}
public void handleServer(SPacketRPCResponseTypeError packet) {
EaglerRPCFutureImpl<ResponseWebViewStatus> future = (EaglerRPCFutureImpl<ResponseWebViewStatus>)waitingRequests.remove(packet.requestID);
if(future != null) {
future.fireExceptionInternal(new EaglerRPCResponseException(packet.errorMessage));
}
}
public void handleServer(SPacketRPCEventWebViewOpenClose packet) {
if((subscribedEvents & EnumSubscribeEvents.EVENT_WEBVIEW_OPEN_CLOSE.bit) != 0) {
fireEventHandlers(new EventWebViewOpenClose(packet.channelName, packet.channelOpen));
}
}
public void handleServer(SPacketRPCEventWebViewMessage packet) {
if((subscribedEvents & EnumSubscribeEvents.EVENT_WEBVIEW_MESSAGE.bit) != 0) {
EventWebViewMessage.MessageType mt;
switch(packet.messageType) {
case SPacketRPCEventWebViewMessage.MESSAGE_TYPE_STRING:
mt = EventWebViewMessage.MessageType.STRING;
break;
case SPacketRPCEventWebViewMessage.MESSAGE_TYPE_BINARY:
mt = EventWebViewMessage.MessageType.BINARY;
break;
default:
return;
}
fireEventHandlers(new EventWebViewMessage(packet.channelName, mt, packet.messageContent));
}
}
public void handleServer(SPacketRPCEventToggledVoice packet) {
if((subscribedEvents & EnumSubscribeEvents.EVENT_TOGGLE_VOICE.bit) != 0) {
EventToggledVoice.VoiceState vsOld = translateVoiceState(packet.oldVoiceState);
EventToggledVoice.VoiceState vsNew = translateVoiceState(packet.newVoiceState);
if(vsOld == null || vsNew == null) {
return;
}
fireEventHandlers(new EventToggledVoice(vsOld, vsNew));
}
}
}
private static EventToggledVoice.VoiceState translateVoiceState(int vs) {
switch(vs) {
case SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_SERVER_DISABLE:
return EventToggledVoice.VoiceState.SERVER_DISABLE;
case SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_DISABLED:
return EventToggledVoice.VoiceState.DISABLED;
case SPacketRPCResponseTypeVoiceStatus.VOICE_STATE_ENABLED:
return EventToggledVoice.VoiceState.ENABLED;
default:
return null;
}
}
private static ResponseVoiceStatus.VoiceState translateVoiceState2(int vs) {
switch(vs) {
case SPacketRPCEventToggledVoice.VOICE_STATE_SERVER_DISABLE:
default:
return ResponseVoiceStatus.VoiceState.SERVER_DISABLE;
case SPacketRPCEventToggledVoice.VOICE_STATE_DISABLED:
return ResponseVoiceStatus.VoiceState.DISABLED;
case SPacketRPCEventToggledVoice.VOICE_STATE_ENABLED:
return ResponseVoiceStatus.VoiceState.ENABLED;
}
}
private static ResponseWebViewStatus.WebViewState translateWebViewState(int vs) {
switch(vs) {
case SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_NOT_SUPPORTED:
default:
return ResponseWebViewStatus.WebViewState.NOT_SUPPORTED;
case SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_SERVER_DISABLE:
return ResponseWebViewStatus.WebViewState.SERVER_DISABLE;
case SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_CHANNEL_CLOSED:
return ResponseWebViewStatus.WebViewState.CHANNEL_CLOSED;
case SPacketRPCResponseTypeWebViewStatus.WEBVIEW_STATE_CHANNEL_OPEN:
return ResponseWebViewStatus.WebViewState.CHANNEL_OPEN;
}
}
@Override
public Player getPlayer() {
return playerObj;
}
@Override
public void sendRPCPacket(EaglerBackendRPCPacket packet) {
if(!open) {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + "] Sent " + packet.getClass().getSimpleName() + " on a dead connection!");
return;
}
if(packet == null) {
throw new NullPointerException("Packet cannot be null!");
}
byte[] ret;
int len = packet.length() + 1;
if(outputStreamLock.tryLock()) {
try {
reusableOutputStream.feedBuffer(new byte[len > 0 ? len : 64]);
try {
protocol.writePacket(dataOutputStream, EaglerBackendRPCProtocol.CLIENT_TO_SERVER, packet);
}catch(IOException ex) {
throw new EaglerRPCException("Failed to serialize packet: " + packet.getClass().getSimpleName(), ex);
}
ret = reusableOutputStream.returnBuffer();
}finally {
outputStreamLock.unlock();
}
}else {
ReusableByteArrayOutputStream bao = new ReusableByteArrayOutputStream();
bao.feedBuffer(new byte[len > 0 ? len : 64]);
try {
protocol.writePacket(new DataOutputStream(bao), EaglerBackendRPCProtocol.CLIENT_TO_SERVER, packet);
}catch(IOException ex) {
throw new EaglerRPCException("Failed to serialize packet: " + packet.getClass().getSimpleName(), ex);
}
ret = bao.returnBuffer();
}
if(len > 0 && len != ret.length) {
EaglerXBukkitAPIPlugin.logger()
.warning("[" + playerObj.getName() + "] Packet type " + packet.getClass().getSimpleName()
+ " was the wrong length after serialization: " + ret.length + " != " + len);
}
playerObj.sendPluginMessage(EaglerXBukkitAPIPlugin.getEagler(), playerDataObj.pluginChName, ret);
}
protected EaglerBackendRPCPacket decodePacket(byte[] data) throws IOException {
EaglerBackendRPCPacket ret;
if(inputStreamLock.tryLock()) {
try {
reusableInputStream.feedBuffer(data);
ret = protocol.readPacket(dataInputStream, EaglerBackendRPCProtocol.SERVER_TO_CLIENT);
}finally {
inputStreamLock.unlock();
}
}else {
ReusableByteArrayInputStream bai = new ReusableByteArrayInputStream();
bai.feedBuffer(data);
ret = protocol.readPacket(new DataInputStream(bai), EaglerBackendRPCProtocol.SERVER_TO_CLIENT);
}
return ret;
}
@Override
public void subscribeEvents(int events) {
int newEvents = subscribedEvents | events;
if(newEvents != subscribedEvents) {
sendRPCPacket(new CPacketRPCSubscribeEvents(newEvents));
subscribedEvents = events;
}
}
@Override
public void unsubscribeEvents(int events) {
int newEvents = subscribedEvents & ~events;
if(newEvents != subscribedEvents) {
sendRPCPacket(new CPacketRPCSubscribeEvents(newEvents));
subscribedEvents = events;
}
}
@Override
public void unsubscribeAllEvents() {
if(subscribedEvents != 0) {
sendRPCPacket(new CPacketRPCSubscribeEvents(0));
subscribedEvents = 0;
}
}
@Override
public int getSubscribedEventsBits() {
return subscribedEvents;
}
@Override
public void addEventListener(EnumSubscribeEvents eventType,
IEaglerRPCEventHandler<? extends IEaglerRPCEvent> handler) {
synchronized(eventHandlers) {
eventHandlers.put(eventType, handler);
}
}
@Override
public void removeEventListener(EnumSubscribeEvents eventType,
IEaglerRPCEventHandler<? extends IEaglerRPCEvent> handler) {
synchronized(eventHandlers) {
eventHandlers.remove(eventType, handler);
}
}
@Override
public void removeEventListeners(EnumSubscribeEvents eventType) {
synchronized(eventHandlers) {
eventHandlers.removeAll(eventType);
}
}
@Override
public void addCloseListener(IEaglerRPCCloseHandler handler) {
synchronized(closeHandlers) {
closeHandlers.add(handler);
}
}
@Override
public void removeCloseListener(IEaglerRPCCloseHandler handler) {
synchronized(closeHandlers) {
closeHandlers.remove(handler);
}
}
@Override
public void removeCloseListeners() {
synchronized(closeHandlers) {
closeHandlers.clear();
}
}
@Override
public boolean redirectPlayerSupported() {
return gameProtocol >= 4;
}
@Override
public void redirectPlayerToWebSocket(String webSocketURI) {
if(gameProtocol >= 4) {
if(webSocketURI == null) {
throw new NullPointerException("URI cannot be null!");
}
sendRPCPacket(new CPacketRPCRedirectPlayer(webSocketURI));
}else {
EaglerXBukkitAPIPlugin.logger()
.warning("[" + playerObj.getName() + "] some plugin tried to redirect player to \"" + webSocketURI
+ "\" but that player's client does not support websocket redirects!");
}
}
@Override
public void setBaseRequestTimeout(int seconds) {
baseTimeout = seconds * 1000;
}
protected <T extends IEaglerRPCResponse> IEaglerRPCFuture<T> requestSendHelper(int type) {
EaglerRPCFutureImpl<T> ret = new EaglerRPCFutureImpl();
ret.setExpiresMSFromNow(baseTimeout);
int rqid = requestIDGenerator.incrementAndGet();
sendRPCPacket(new CPacketRPCRequestPlayerInfo(rqid, type));
waitingRequests.put(rqid, ret);
return ret;
}
@Override
public IEaglerRPCFuture<ResponseUUID> requestPlayerProfileUUID() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_REAL_UUID);
}
@Override
public IEaglerRPCFuture<ResponseString> requestPlayerRealIP() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_REAL_IP);
}
@Override
public IEaglerRPCFuture<ResponseString> requestPlayerOrigin() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_ORIGIN);
}
@Override
public IEaglerRPCFuture<ResponseString> requestPlayerUserAgent() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_USER_AGENT);
}
@Override
public IEaglerRPCFuture<ResponseBytes> requestPlayerSkinData() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_SKIN_DATA);
}
@Override
public IEaglerRPCFuture<ResponseBytes> requestPlayerCapeData() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CAPE_DATA);
}
@Override
public IEaglerRPCFuture<ResponseCookie> requestPlayerCookieData() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_COOKIE);
}
@Override
public IEaglerRPCFuture<ResponseString> requestPlayerClientBrandStr() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_BRAND_STR);
}
@Override
public IEaglerRPCFuture<ResponseString> requestPlayerClientVersionStr() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_VERSION_STR);
}
@Override
public IEaglerRPCFuture<ResponseString> requestPlayerClientBrandAndVersionStr() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_BRAND_VERSION_STR);
}
@Override
public IEaglerRPCFuture<ResponseUUID> requestPlayerClientBrandUUID() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_BRAND_UUID);
}
@Override
public IEaglerRPCFuture<ResponseVoiceStatus> requestPlayerVoiceStatus() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_VOICE_STATUS);
}
@Override
public IEaglerRPCFuture<ResponseWebViewStatus> requestPlayerWebViewStatus() {
return requestSendHelper(CPacketRPCRequestPlayerInfo.REQUEST_PLAYER_CLIENT_WEBVIEW_STATUS);
}
protected void cleanupTimedOutRequests(long now) {
if(!waitingRequests.isEmpty()) {
List<EaglerRPCFutureImpl<? extends IEaglerRPCResponse>> expired = null;
Iterator<EaglerRPCFutureImpl<? extends IEaglerRPCResponse>> itr = waitingRequests.values().iterator();
while(itr.hasNext()) {
EaglerRPCFutureImpl<? extends IEaglerRPCResponse> itm = itr.next();
if(itm.hasExpiredBetter(now)) {
if(expired == null) {
expired = new ArrayList<>(4);
}
expired.add(itm);
try {
itr.remove();
}catch(Throwable t) {
}
}
}
if(expired != null) {
for(int i = 0, l = expired.size(); i < l; ++i) {
try {
EaglerRPCFutureImpl<? extends IEaglerRPCResponse> itm = expired.get(i);
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName() + "] An RPC request timed out before it could be completed!");
itm.fireExceptionInternal(new EaglerRPCTimeoutException("The request was not completed in time!"));
}catch(Throwable t) {
EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, "[" + playerObj.getName() + "] An unhandled exception was thrown while firing request timeout signal!", t);
}
}
}
}
}
@Override
public void sendRawCustomPayloadPacket(String channel, byte[] data) {
sendRPCPacket(new CPacketRPCSendRawMessage(channel, data));
}
@Override
public boolean pauseMenuCustomizationSupported() {
return gameProtocol >= 4;
}
@Override
public void setPauseMenuCustomizationState(CPacketRPCSetPauseMenuCustom packet) {
if(gameProtocol >= 4) {
sendRPCPacket(packet);
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to configure pause menu customization, but the player's client does not support that feature!");
}
}
@Override
public void sendWebViewMessageString(String channelName, String data) {
if(gameProtocol >= 4) {
if(channelName == null) {
throw new NullPointerException("Channel cannot be null!");
}
if(data == null) {
throw new NullPointerException("Data cannot be null!");
}
sendRPCPacket(new CPacketRPCSendWebViewMessage(channelName,
CPacketRPCSendWebViewMessage.MESSAGE_TYPE_STRING, data.getBytes(StandardCharsets.UTF_8)));
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to send a webview channel message, but the player's client does not support that feature!");
}
}
@Override
public void sendWebViewMessageBytes(String channelName, byte[] data) {
if(gameProtocol >= 4) {
if(channelName == null) {
throw new NullPointerException("Channel cannot be null!");
}
if(data == null) {
throw new NullPointerException("Data cannot be null!");
}
sendRPCPacket(new CPacketRPCSendWebViewMessage(channelName, CPacketRPCSendWebViewMessage.MESSAGE_TYPE_BINARY, data));
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to send a webview channel message, but the player's client does not support that feature!");
}
}
@Override
public void forcePlayerSkin(byte[] skinData, boolean notifyOthers) {
if(skinData == null) {
throw new NullPointerException("Skin data cannot be null!");
}
if(skinData.length > 32720) {
throw new IllegalArgumentException("Skin data cannot be more than 32720 bytes!");
}
sendRPCPacket(new CPacketRPCSetPlayerSkin(notifyOthers, skinData));
}
@Override
public void forcePlayerCape(byte[] capeData, boolean notifyOthers) {
if(capeData == null) {
throw new NullPointerException("Cape data cannot be null!");
}
if(capeData.length > 32720) {
throw new IllegalArgumentException("Cape data cannot be more than 32720 bytes!");
}
sendRPCPacket(new CPacketRPCSetPlayerCape(notifyOthers, capeData));
}
@Override
public void setCookieData(byte[] cookieData, int expiresAfterSec, boolean revokeQuerySupported,
boolean saveToDisk) {
if(gameProtocol >= 4) {
sendRPCPacket(new CPacketRPCSetPlayerCookie(revokeQuerySupported, saveToDisk, expiresAfterSec, cookieData));
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to set a cookie, but the player's client does not support that feature!");
}
}
@Override
public void setFNAWSkinsEnabled(boolean enabled, boolean force) {
sendRPCPacket(new CPacketRPCSetPlayerFNAWEn(enabled, force));
}
@Override
public void setFNAWSkinsEnabled(boolean enabled) {
sendRPCPacket(new CPacketRPCSetPlayerFNAWEn(enabled, false));
}
@Override
public void resetForcedMulti(boolean resetSkin, boolean resetCape, boolean resetFNAWForce, boolean notifyOtherPlayers) {
sendRPCPacket(new CPacketRPCResetPlayerMulti(resetSkin, resetCape, resetFNAWForce, notifyOtherPlayers));
}
@Override
public void resetForcedSkin(boolean notifyOtherPlayers) {
sendRPCPacket(new CPacketRPCResetPlayerMulti(true, false, false, notifyOtherPlayers));
}
@Override
public void resetForcedCape(boolean notifyOtherPlayers) {
sendRPCPacket(new CPacketRPCResetPlayerMulti(false, true, false, notifyOtherPlayers));
}
@Override
public void resetForcedFNAW() {
sendRPCPacket(new CPacketRPCResetPlayerMulti(false, false, true, false));
}
@Override
public boolean notifSupported() {
return gameProtocol >= 4;
}
@Override
public void notifIconRegister(Map<UUID, PacketImageData> iconsToRegister) {
if(gameProtocol >= 4) {
sendRPCPacket(new CPacketRPCNotifIconRegister(iconsToRegister));
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to register notification icons, but the player's client does not support that feature!");
}
}
@Override
public void notifIconRegister(UUID iconUUID, PacketImageData imageData) {
if(gameProtocol >= 4) {
Map<UUID, PacketImageData> toReg = new HashMap<>(1);
toReg.put(iconUUID, imageData);
sendRPCPacket(new CPacketRPCNotifIconRegister(toReg));
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to register notification icons, but the player's client does not support that feature!");
}
}
@Override
public void notifIconRelease(Collection<UUID> iconsToRelease) {
if(gameProtocol >= 4) {
sendRPCPacket(new CPacketRPCNotifIconRelease(iconsToRelease));
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to release notification icons, but the player's client does not support that feature!");
}
}
@Override
public void notifIconRelease(UUID iconUUID) {
if(gameProtocol >= 4) {
sendRPCPacket(new CPacketRPCNotifIconRelease(Arrays.asList(iconUUID)));
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to release notification icons, but the player's client does not support that feature!");
}
}
@Override
public void notifBadgeShow(CPacketRPCNotifBadgeShow packet) {
if(gameProtocol >= 4) {
sendRPCPacket(packet);
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to show notification badge, but the player's client does not support that feature!");
}
}
@Override
public void notifBadgeHide(UUID badgeUUID) {
if(gameProtocol >= 4) {
sendRPCPacket(new CPacketRPCNotifBadgeHide(badgeUUID));
}else {
EaglerXBukkitAPIPlugin.logger().warning("[" + playerObj.getName()
+ "] some plugin tried to hide notification badge, but the player's client does not support that feature!");
}
}
@Override
public <T> void setMeta(String key, T value) {
if(key == null) {
throw new NullPointerException("Key cannot be null!");
}
metadata.put(key, value);
}
@Override
public <T> T getMeta(String key) {
if(key == null) {
throw new NullPointerException("Key cannot be null!");
}
return (T)metadata.get(key);
}
}

View File

@ -1,42 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.client.CPacketRPCEnabled;
public class HelloPacketFactory {
public static final byte[] BASE_HELLO_PACKET;
static {
try {
ByteArrayOutputStream bao = new ByteArrayOutputStream();
DataOutputStream dao = new DataOutputStream(bao);
CPacketRPCEnabled pkt = new CPacketRPCEnabled(new int[] { EaglerBackendRPCProtocol.V1.vers });
EaglerBackendRPCProtocol.INIT.writePacket(dao, EaglerBackendRPCProtocol.CLIENT_TO_SERVER, pkt);
BASE_HELLO_PACKET = bao.toByteArray();
}catch(IOException ex) {
throw new RuntimeException(ex);
}
}
}

View File

@ -1,200 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
import org.bukkit.entity.Player;
import org.bukkit.metadata.LazyMetadataValue;
import org.bukkit.metadata.MetadataValue;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.EaglerBackendRPCPacket;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEnabledFailure;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.pkt.server.SPacketRPCEnabledSuccess;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.EaglerXBukkitAPIPlugin;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCException;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.EaglerRPCInitException;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.IEaglerXBukkitAPI;
import net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.api.response.EaglerRPCTimeoutException;
public class PlayerDataObj {
public static final String METADATA_BASE = "EXRPC_PDataObj";
public final Player player;
public String pluginChName = null;
public volatile boolean hasRecievedReady = false;
public volatile boolean isSupported = true;
public volatile EaglerXBukkitImpl currentAPI = null;
public volatile EaglerRPCFutureImpl<IEaglerXBukkitAPI> openFuture = null;
public static PlayerDataObj getForPlayer(Player player) {
List<MetadataValue> vigg = player.getMetadata(METADATA_BASE);
return !vigg.isEmpty() ? (PlayerDataObj)vigg.get(0).value() : null;
}
public static void setupPlayer(Player player) {
player.setMetadata(METADATA_BASE, new LazyMetadataValue(EaglerXBukkitAPIPlugin.getEagler(), () -> new PlayerDataObj(player)));
}
protected PlayerDataObj(Player player) {
this.player = player;
}
public void firePluginReadyMsgRecieved(boolean modern) {
synchronized(this) {
if(!hasRecievedReady) {
hasRecievedReady = true;
pluginChName = modern ? EaglerBackendRPCProtocol.CHANNEL_NAME_MODERN : EaglerBackendRPCProtocol.CHANNEL_NAME;
if(openFuture != null) {
EaglerXBukkitImpl.sendHelloPacket(pluginChName, player);
}
}
}
}
public void firePluginMsgRecievedInternal(byte[] data) {
EaglerXBukkitImpl apiObj = null;
synchronized(this) {
if(openFuture != null) {
try {
handleOpenResult(openFuture, data);
}finally {
openFuture = null;
}
}else if(currentAPI != null) {
apiObj = currentAPI;
}
}
if(apiObj != null) {
handleAPIMessage(apiObj, data);
}
}
public void firePlayerQuitEventInternal() {
synchronized(this) {
if(openFuture != null) {
try {
openFuture.fireExceptionInternal(new EaglerRPCException("Player quit before the connection could be established!"));
}finally {
openFuture = null;
}
}else if(currentAPI != null) {
currentAPI.fireAPIClosedEventInternal();
}
}
}
private void handleOpenResult(EaglerRPCFutureImpl<IEaglerXBukkitAPI> apiFuture, byte[] data) {
EaglerBackendRPCPacket ret;
try {
ByteArrayInputStream bis = new ByteArrayInputStream(data);
ret = EaglerBackendRPCProtocol.INIT.readPacket(new DataInputStream(bis), EaglerBackendRPCProtocol.SERVER_TO_CLIENT);
if(bis.available() > 0) {
throw new IOException("There were " + bis.available() + " bytes available after reading packet \"" + ret.getClass().getSimpleName() + "\"!");
}
}catch(IOException ex) {
EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, "[" + player.getName() + "] Could not parse incoming RPC packet from bungee/velocity server! (protocol: INIT)", ex);
apiFuture.fireExceptionInternal(ex);
return;
}
if(ret instanceof SPacketRPCEnabledSuccess) {
SPacketRPCEnabledSuccess pkt = (SPacketRPCEnabledSuccess)ret;
if(pkt.selectedRPCProtocol != EaglerBackendRPCProtocol.V1.vers) {
try {
// send raw CPacketRPCDisabled
player.sendPluginMessage(EaglerXBukkitAPIPlugin.getEagler(), pluginChName, new byte[] { 0x03 });
}finally {
apiFuture.fireExceptionInternal(new EaglerRPCException("Server tried to select an unsupported protocol: " + pkt.selectedRPCProtocol));
}
}else {
currentAPI = EaglerXBukkitImpl.createFromHandshakeInternal(this, pkt);
apiFuture.fireCompleteInternal(currentAPI);
}
}else if(ret instanceof SPacketRPCEnabledFailure) {
SPacketRPCEnabledFailure pkt = (SPacketRPCEnabledFailure)ret;
String msg = "Server responded with failure code: ";
switch(pkt.failureCode) {
case SPacketRPCEnabledFailure.FAILURE_CODE_NOT_ENABLED:
msg += "FAILURE_CODE_NOT_ENABLED";
break;
case SPacketRPCEnabledFailure.FAILURE_CODE_NOT_EAGLER_PLAYER:
msg += "FAILURE_CODE_NOT_EAGLER_PLAYER";
break;
case SPacketRPCEnabledFailure.FAILURE_CODE_OUTDATED_SERVER:
msg += "FAILURE_CODE_OUTDATED_SERVER";
break;
case SPacketRPCEnabledFailure.FAILURE_CODE_OUTDATED_CLIENT:
msg += "FAILURE_CODE_OUTDATED_CLIENT";
break;
case SPacketRPCEnabledFailure.FAILURE_CODE_INTERNAL_ERROR:
msg += "FAILURE_CODE_INTERNAL_ERROR";
break;
default:
msg += pkt.failureCode;
break;
}
apiFuture.fireExceptionInternal(new EaglerRPCInitException(pkt.failureCode, msg));
}else {
EaglerXBukkitAPIPlugin.logger().severe("[" + player.getName() + "] Unknown response type from bungee/velocity to API open request: " + ret.getClass().getSimpleName());
}
}
private void handleAPIMessage(EaglerXBukkitImpl apiObj, byte[] data) {
EaglerBackendRPCPacket ret;
try {
ret = apiObj.decodePacket(data);
}catch(IOException ex) {
EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, "[" + player.getName() + "] Could not parse incoming RPC packet from bungee/velocity server! (protocol: " + apiObj.getRPCProtocolVersion() + ")", ex);
return;
}
apiObj.fireAPIPacketRecievedInternal(ret);
}
public void fireCheckRequestTimeoutsInternal(long now) {
EaglerXBukkitImpl apiObj;
synchronized(this) {
if(openFuture != null) {
if(openFuture.hasExpiredBetter(now)) {
try {
EaglerXBukkitAPIPlugin.logger().warning("[" + player.getName() + "] An RPC open request timed out before it could be completed!");
openFuture.fireExceptionInternal(new EaglerRPCTimeoutException("The request was not completed in time!"));
}catch(Throwable t) {
EaglerXBukkitAPIPlugin.logger().log(Level.SEVERE, "[" + player.getName() + "] An unhandled exception was thrown while firing request timeout signal!", t);
}finally {
openFuture = null;
}
}
return;
}else {
apiObj = currentAPI;
}
}
if(apiObj != null) {
apiObj.cleanupTimedOutRequests(now);
}
}
}

View File

@ -1,81 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl;
import java.io.IOException;
import java.io.InputStream;
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;
}
}

View File

@ -1,82 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
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;
}
}

View File

@ -1,41 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.impl;
import java.util.concurrent.Executor;
import com.google.common.util.concurrent.MoreExecutors;
public class SameThreadExecutor {
public static final Executor SAME_THREAD_EXECUTOR;
static {
Executor fuck;
try {
fuck = (Executor) MoreExecutors.class.getDeclaredMethod("newDirectExecutorService").invoke(null);
}catch(Throwable t) {
try {
fuck = (Executor) MoreExecutors.class.getDeclaredMethod("sameThreadExecutor").invoke(null);
}catch(Throwable t2) {
throw new RuntimeException("Google fucked up!", t2);
}
}
SAME_THREAD_EXECUTOR = fuck;
}
}

View File

@ -1,6 +0,0 @@
name: EaglercraftXBukkitAPI
version: 1.0.1
main: net.lax1dude.eaglercraft.v1_8.plugin.bukkit_rpc_helper.EaglerXBukkitAPIPlugin
description: Official EaglercraftX API for Bukkit servers
author: lax1dude
api-version: 1.8

View File

@ -1,11 +0,0 @@
lib/*
.idea/*
*.iml
out/*
deps/BungeeCord.jar
/.gradle/
/.settings/
.classpath
.project
/build/
/bin/

View File

@ -1,43 +0,0 @@
plugins {
id 'java'
id 'eclipse'
}
group = 'net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord'
version = ''
repositories {
mavenCentral()
}
sourceSets {
main {
java {
srcDirs(
'src/main/java',
'../../sources/protocol-game/java',
'../backend-rpc-protocol/src/backend-rpc-protocol/java'
)
}
}
}
dependencies {
implementation files('deps/BungeeCord.jar')
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
jar {
compileJava.options.encoding = 'UTF-8'
javadoc.options.encoding = 'UTF-8'
manifest {
attributes(
'Main-Class': 'net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.shit.MainClass'
)
}
}

View File

@ -1,3 +0,0 @@
Place the BungeeCord JAR file in this folder, name it "BungeeCord.jar"
You can download it from here: https://ci.md-5.net/job/BungeeCord/

View File

@ -1,6 +0,0 @@
#Thu Jun 20 10:14:47 CDT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,234 +0,0 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
warn () {
echo "$*"
} >&2
die () {
echo
echo "$*"
echo
exit 1
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

View File

@ -1,89 +0,0 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -1,9 +0,0 @@
Plugin for eaglercraft on bungeecord
Not using gradle to give more direct access to bungeecord's internals, as gradle only provides a dummy jar containing the api.
EaglercraftXBungee requires netty's websocket client/server, which is already in the production bungeecord jar so it's ideal to compile directly against the real jar
Simply link "src/main/java" and "src/main/resources" as source folders, and then add the latest version of bungeecord jar for minecraft 1.8 to the build path.
To build, export the source folders as a JAR and export the JAR to contain all the classes found in the JARs in "deps" within it, but not including the classes from the actual bungeecord jar

View File

@ -1,2 +0,0 @@
rootProject.name = 'EaglercraftXBungee'

View File

@ -1,387 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.DefaultAuthSystem;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandClientBrand;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandConfirmCode;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandDomain;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandEaglerPurge;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandEaglerRegister;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command.CommandRatelimit;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerAuthConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerBungeeConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.handlers.EaglerPacketEventListener;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.handlers.EaglerPluginEventListener;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerPipeline;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerUpdateSvc;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpWebServer;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.shit.CompatWarning;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.BinaryHttpClient;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.CapeServiceOffline;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.ISkinService;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinServiceOffline;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceService;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.GamePluginMessageProtocol;
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginDescription;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.BungeeCord;
public class EaglerXBungee extends Plugin {
public static final String NATIVE_BUNGEECORD_BUILD = "1.21-R0.1-SNAPSHOT:0aa2871:1893";
public static final String NATIVE_BUNGEECORD_BUILD_DL = "https://ci.md-5.net/job/BungeeCord/1893/artifact/bootstrap/target/BungeeCord.jar";
public static final String NATIVE_WATERFALL_BUILD = "1.21-R0.1-SNAPSHOT:9ab9e2b:582";
public static final String NATIVE_WATERFALL_BUILD_DL = "https://api.papermc.io/v2/projects/waterfall/versions/1.21/builds/582/downloads/waterfall-1.21-582.jar";
static {
CompatWarning.displayCompatWarning();
}
private static EaglerXBungee instance = null;
private EaglerBungeeConfig conf = null;
private EventLoopGroup eventLoopGroup;
private EventLoopGroup eventLoopGroupBoss;
private Collection<Channel> openChannels;
private Timer closeInactiveConnections = null;
private Timer skinServiceTasks = null;
private Timer authServiceTasks = null;
private Timer updateServiceTasks = null;
private final ChannelFutureListener newChannelListener;
private ISkinService skinService;
private CapeServiceOffline capeService;
private VoiceService voiceService;
private DefaultAuthSystem defaultAuthSystem;
public EaglerXBungee() {
instance = this;
openChannels = new LinkedList<>();
newChannelListener = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture ch) throws Exception {
synchronized(openChannels) { // synchronize whole block to preserve logging order
if(ch.isSuccess()) {
EaglerXBungee.logger().info("Eaglercraft is listening on: " + ch.channel().attr(EaglerPipeline.LOCAL_ADDRESS).get().toString());
openChannels.add(ch.channel());
}else {
EaglerXBungee.logger().severe("Eaglercraft could not bind port: " + ch.channel().attr(EaglerPipeline.LOCAL_ADDRESS).get().toString());
EaglerXBungee.logger().severe("Reason: " + ch.cause().toString());
}
}
}
};
}
@Override
public void onLoad() {
Map<String, String> templateGlobals = EaglerXBungeeAPIHelper.getTemplateGlobals();
PluginDescription desc = this.getDescription();
templateGlobals.put("plugin_name", desc.getName());
templateGlobals.put("plugin_version", desc.getVersion());
templateGlobals.put("plugin_authors", desc.getAuthor());
templateGlobals.put("plugin_description", desc.getDescription());
try {
eventLoopGroup = ((BungeeCord) getProxy()).eventLoops;
} catch (NoSuchFieldError e) {
try {
eventLoopGroup = (EventLoopGroup) BungeeCord.class.getField("workerEventLoopGroup").get(getProxy());
eventLoopGroupBoss = (EventLoopGroup) BungeeCord.class.getField("bossEventLoopGroup").get(getProxy());
} catch (IllegalAccessException | NoSuchFieldException ex) {
throw new RuntimeException(ex);
}
}
reloadConfig();
}
@Override
public void onEnable() {
PluginManager mgr = getProxy().getPluginManager();
mgr.registerListener(this, new EaglerPluginEventListener(this));
mgr.registerListener(this, new EaglerPacketEventListener(this));
mgr.registerCommand(this, new CommandRatelimit());
mgr.registerCommand(this, new CommandConfirmCode());
mgr.registerCommand(this, new CommandDomain());
mgr.registerCommand(this, new CommandClientBrand());
EaglerAuthConfig authConf = conf.getAuthConfig();
conf.setCracked(!BungeeCord.getInstance().getConfig().isOnlineMode() || !authConf.isEnableAuthentication());
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
if(!BungeeCord.getInstance().getConfig().isOnlineMode()) {
getLogger().severe("Online mode is set to false! Authentication system has been disabled");
authConf.triggerOnlineModeDisabled();
}else {
mgr.registerCommand(this, new CommandEaglerRegister(authConf.getEaglerCommandName()));
mgr.registerCommand(this, new CommandEaglerPurge(authConf.getEaglerCommandName()));
}
}
for(String str : GamePluginMessageProtocol.getAllChannels()) {
getProxy().registerChannel(str);
}
getProxy().registerChannel(EaglerBackendRPCProtocol.CHANNEL_NAME);
getProxy().registerChannel(EaglerBackendRPCProtocol.CHANNEL_NAME_READY);
getProxy().registerChannel(EaglerBackendRPCProtocol.CHANNEL_NAME_MODERN);
getProxy().registerChannel(EaglerBackendRPCProtocol.CHANNEL_NAME_READY_MODERN);
getProxy().registerChannel(EaglerPacketEventListener.GET_DOMAIN_CHANNEL);
startListeners();
if(closeInactiveConnections != null) {
closeInactiveConnections.cancel();
closeInactiveConnections = null;
}
if(skinServiceTasks != null) {
skinServiceTasks.cancel();
skinServiceTasks = null;
}
closeInactiveConnections = new Timer("EaglerXBungee: Network Tick Tasks");
closeInactiveConnections.scheduleAtFixedRate(EaglerPipeline.closeInactive, 0l, 250l);
boolean downloadSkins = conf.getDownloadVanillaSkins();
if(downloadSkins) {
if(skinService == null) {
skinService = new SkinService();
}else if(skinService instanceof SkinServiceOffline) {
skinService.shutdown();
skinService = new SkinService();
}
} else {
if(skinService == null) {
skinService = new SkinServiceOffline();
}else if(skinService instanceof SkinService) {
skinService.shutdown();
skinService = new SkinServiceOffline();
}
}
skinService.init(conf.getSkinCacheURI(), conf.getSQLiteDriverClass(), conf.getSQLiteDriverPath(),
conf.getKeepObjectsDays(), conf.getKeepProfilesDays(), conf.getMaxObjects(), conf.getMaxProfiles());
if(skinService instanceof SkinService) {
skinServiceTasks = new Timer("EaglerXBungee: Skin Service Tasks");
skinServiceTasks.schedule(new TimerTask() {
@Override
public void run() {
try {
skinService.flush();
}catch(Throwable t) {
logger().log(Level.SEVERE, "Error flushing skin cache!", t);
}
}
}, 1000l, 1000l);
}
capeService = new CapeServiceOffline();
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
try {
defaultAuthSystem = DefaultAuthSystem.initializeAuthSystem(authConf);
}catch(DefaultAuthSystem.AuthSystemException ex) {
logger().log(Level.SEVERE, "Could not load authentication system!", ex);
}
if(defaultAuthSystem != null) {
authServiceTasks = new Timer("EaglerXBungee: Auth Service Tasks");
authServiceTasks.schedule(new TimerTask() {
@Override
public void run() {
try {
defaultAuthSystem.flush();
}catch(Throwable t) {
logger().log(Level.SEVERE, "Error flushing auth cache!", t);
}
}
}, 60000l, 60000l);
}
}
if(conf.getEnableVoiceChat()) {
voiceService = new VoiceService(conf);
logger().warning("Voice chat enabled, not recommended for public servers!");
}else {
logger().info("Voice chat disabled, add \"allow_voice: true\" to your listeners to enable");
}
if(updateServiceTasks != null) {
updateServiceTasks.cancel();
updateServiceTasks = null;
}
if(!conf.getUpdateConfig().isBlockAllClientUpdates()) {
updateServiceTasks = new Timer("EaglerXBungee: Update Service Tasks");
updateServiceTasks.schedule(new TimerTask() {
@Override
public void run() {
try {
EaglerUpdateSvc.updateTick();
}catch(Throwable t) {
logger().log(Level.SEVERE, "Error ticking update service!", t);
}
}
}, 0l, 5000l);
}
}
@Override
public void onDisable() {
stopListeners();
PluginManager mgr = getProxy().getPluginManager();
mgr.unregisterListeners(this);
mgr.unregisterCommands(this);
for(String str : GamePluginMessageProtocol.getAllChannels()) {
getProxy().unregisterChannel(str);
}
getProxy().unregisterChannel(EaglerBackendRPCProtocol.CHANNEL_NAME);
getProxy().unregisterChannel(EaglerBackendRPCProtocol.CHANNEL_NAME_READY);
getProxy().unregisterChannel(EaglerBackendRPCProtocol.CHANNEL_NAME_MODERN);
getProxy().unregisterChannel(EaglerBackendRPCProtocol.CHANNEL_NAME_READY_MODERN);
getProxy().unregisterChannel(EaglerPacketEventListener.GET_DOMAIN_CHANNEL);
if(closeInactiveConnections != null) {
closeInactiveConnections.cancel();
closeInactiveConnections = null;
}
if(skinServiceTasks != null) {
skinServiceTasks.cancel();
skinServiceTasks = null;
}
if(updateServiceTasks != null) {
updateServiceTasks.cancel();
updateServiceTasks = null;
}
if(skinService != null) {
skinService.shutdown();
skinService = null;
}
if(capeService != null) {
capeService.shutdown();
capeService = null;
}
if(defaultAuthSystem != null) {
defaultAuthSystem.destroy();
defaultAuthSystem = null;
if(authServiceTasks != null) {
authServiceTasks.cancel();
authServiceTasks = null;
}
}
voiceService = null;
BinaryHttpClient.killEventLoop();
}
public void reload() {
stopListeners();
reloadConfig();
startListeners();
}
private void reloadConfig() {
try {
conf = EaglerBungeeConfig.loadConfig(getDataFolder());
if(conf == null) {
throw new IOException("Config failed to parse!");
}
conf.setCracked(!BungeeCord.getInstance().getConfig().isOnlineMode() || !conf.getAuthConfig().isEnableAuthentication());
HttpWebServer.regenerate404Pages();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
public void startListeners() {
for(EaglerListenerConfig conf : conf.getServerListeners()) {
if(conf.getAddress() != null) {
makeListener(conf, conf.getAddress());
}
if(conf.getAddressV6() != null) {
makeListener(conf, conf.getAddressV6());
}
}
}
private void makeListener(EaglerListenerConfig confData, InetSocketAddress addr) {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.option(ChannelOption.SO_REUSEADDR, true)
.childOption(ChannelOption.TCP_NODELAY, true)
.channel(PipelineUtils.getServerChannel(addr));
if(eventLoopGroupBoss != null) {
bootstrap.group(eventLoopGroupBoss, eventLoopGroup);
}else {
bootstrap.group(eventLoopGroup);
}
bootstrap.childAttr(EaglerPipeline.LISTENER, confData)
.attr(EaglerPipeline.LOCAL_ADDRESS, addr)
.localAddress(addr)
.childHandler(EaglerPipeline.SERVER_CHILD)
.bind().addListener(newChannelListener);
}
public void stopListeners() {
synchronized(openChannels) {
for(Channel c : openChannels) {
c.close().syncUninterruptibly();
EaglerXBungee.logger().info("Eaglercraft listener closed: " + c.attr(EaglerPipeline.LOCAL_ADDRESS).get().toString());
}
openChannels.clear();
}
synchronized(EaglerPipeline.openChannels) {
EaglerPipeline.openChannels.clear();
}
}
public EaglerBungeeConfig getConfig() {
return conf;
}
public EventLoopGroup getEventLoopGroup() {
return eventLoopGroup;
}
public ISkinService getSkinService() {
return skinService;
}
public CapeServiceOffline getCapeService() {
return capeService;
}
public DefaultAuthSystem getAuthService() {
return defaultAuthSystem;
}
public VoiceService getVoiceService() {
return voiceService;
}
public static EaglerXBungee getEagler() {
return instance;
}
public static Logger logger() {
return instance.getLogger();
}
}

View File

@ -1,23 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api;
public enum EnumVoiceState {
SERVER_DISABLE,
DISABLED,
ENABLED;
}

View File

@ -1,24 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api;
public enum EnumWebViewState {
NOT_SUPPORTED,
SERVER_DISABLE,
CHANNEL_CLOSED,
CHANNEL_OPEN;
}

View File

@ -1,363 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api;
import java.util.UUID;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketNotifBadgeShowV4EAG.EnumBadgePriority;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.chat.ComponentSerializer;
public class NotificationBadgeBuilder {
public static enum BadgePriority {
LOW, NORMAL, HIGHER, HIGHEST;
}
private UUID badgeUUID = null;
private BaseComponent bodyComponent = null;
private BaseComponent titleComponent = null;
private BaseComponent sourceComponent = null;
private long originalTimestampSec = 0l;
private boolean silent = false;
private BadgePriority priority = BadgePriority.NORMAL;
private UUID mainIconUUID = null;
private UUID titleIconUUID = null;
private int hideAfterSec = 10;
private int expireAfterSec = 3600;
private int backgroundColor = 0xFFFFFF;
private int bodyTxtColor = 0xFFFFFF;
private int titleTxtColor = 0xFFFFFF;
private int sourceTxtColor = 0xFFFFFF;
private SPacketNotifBadgeShowV4EAG packetCache = null;
private boolean packetDirty = true;
public NotificationBadgeBuilder() {
originalTimestampSec = System.currentTimeMillis() / 1000l;
}
public NotificationBadgeBuilder(NotificationBadgeBuilder builder) {
badgeUUID = builder.badgeUUID;
bodyComponent = builder.bodyComponent;
titleComponent = builder.titleComponent;
sourceComponent = builder.sourceComponent;
originalTimestampSec = builder.originalTimestampSec;
silent = builder.silent;
priority = builder.priority;
mainIconUUID = builder.mainIconUUID;
titleIconUUID = builder.titleIconUUID;
hideAfterSec = builder.hideAfterSec;
backgroundColor = builder.backgroundColor;
bodyTxtColor = builder.bodyTxtColor;
titleTxtColor = builder.titleTxtColor;
sourceTxtColor = builder.sourceTxtColor;
packetCache = !builder.packetDirty ? builder.packetCache : null;
packetDirty = builder.packetDirty;
}
public NotificationBadgeBuilder(SPacketNotifBadgeShowV4EAG packet) {
badgeUUID = new UUID(packet.badgeUUIDMost, packet.badgeUUIDLeast);
try {
bodyComponent = ComponentSerializer.deserialize(packet.bodyComponent);
}catch(Throwable t) {
bodyComponent = new TextComponent(packet.bodyComponent);
}
try {
titleComponent = ComponentSerializer.deserialize(packet.titleComponent);
}catch(Throwable t) {
titleComponent = new TextComponent(packet.titleComponent);
}
try {
sourceComponent = ComponentSerializer.deserialize(packet.sourceComponent);
}catch(Throwable t) {
sourceComponent = new TextComponent(packet.sourceComponent);
}
originalTimestampSec = packet.originalTimestampSec;
silent = packet.silent;
switch(packet.priority) {
case LOW:
default:
priority = BadgePriority.LOW;
break;
case NORMAL:
priority = BadgePriority.NORMAL;
break;
case HIGHER:
priority = BadgePriority.HIGHER;
break;
case HIGHEST:
priority = BadgePriority.HIGHEST;
break;
}
mainIconUUID = new UUID(packet.mainIconUUIDMost, packet.mainIconUUIDLeast);
titleIconUUID = new UUID(packet.titleIconUUIDMost, packet.titleIconUUIDLeast);
hideAfterSec = packet.hideAfterSec;
backgroundColor = packet.backgroundColor;
bodyTxtColor = packet.bodyTxtColor;
titleTxtColor = packet.titleTxtColor;
sourceTxtColor = packet.sourceTxtColor;
packetCache = packet;
packetDirty = false;
}
public UUID getBadgeUUID() {
return badgeUUID;
}
public NotificationBadgeBuilder setBadgeUUID(UUID badgeUUID) {
this.badgeUUID = badgeUUID;
this.packetDirty = true;
return this;
}
public NotificationBadgeBuilder setBadgeUUIDRandom() {
this.badgeUUID = UUID.randomUUID();
this.packetDirty = true;
return this;
}
public BaseComponent getBodyComponent() {
return bodyComponent;
}
public NotificationBadgeBuilder setBodyComponent(BaseComponent bodyComponent) {
this.bodyComponent = bodyComponent;
this.packetDirty = true;
return this;
}
public NotificationBadgeBuilder setBodyComponent(String bodyText) {
this.bodyComponent = new TextComponent(bodyText);
this.packetDirty = true;
return this;
}
public BaseComponent getTitleComponent() {
return titleComponent;
}
public NotificationBadgeBuilder setTitleComponent(BaseComponent titleComponent) {
this.titleComponent = titleComponent;
this.packetDirty = true;
return this;
}
public NotificationBadgeBuilder setTitleComponent(String titleText) {
this.titleComponent = new TextComponent(titleText);
this.packetDirty = true;
return this;
}
public BaseComponent getSourceComponent() {
return sourceComponent;
}
public NotificationBadgeBuilder setSourceComponent(BaseComponent sourceComponent) {
this.sourceComponent = sourceComponent;
this.packetDirty = true;
return this;
}
public NotificationBadgeBuilder setSourceComponent(String sourceText) {
this.sourceComponent = new TextComponent(sourceText);
this.packetDirty = true;
return this;
}
public long getOriginalTimestampSec() {
return originalTimestampSec;
}
public NotificationBadgeBuilder setOriginalTimestampSec(long originalTimestampSec) {
this.originalTimestampSec = originalTimestampSec;
this.packetDirty = true;
return this;
}
public boolean isSilent() {
return silent;
}
public NotificationBadgeBuilder setSilent(boolean silent) {
this.silent = silent;
this.packetDirty = true;
return this;
}
public BadgePriority getPriority() {
return priority;
}
public NotificationBadgeBuilder setPriority(BadgePriority priority) {
this.priority = priority;
this.packetDirty = true;
return this;
}
public UUID getMainIconUUID() {
return mainIconUUID;
}
public NotificationBadgeBuilder setMainIconUUID(UUID mainIconUUID) {
this.mainIconUUID = mainIconUUID;
this.packetDirty = true;
return this;
}
public UUID getTitleIconUUID() {
return titleIconUUID;
}
public NotificationBadgeBuilder setTitleIconUUID(UUID titleIconUUID) {
this.titleIconUUID = titleIconUUID;
this.packetDirty = true;
return this;
}
public int getHideAfterSec() {
return hideAfterSec;
}
public NotificationBadgeBuilder setHideAfterSec(int hideAfterSec) {
this.hideAfterSec = hideAfterSec;
this.packetDirty = true;
return this;
}
public int getExpireAfterSec() {
return expireAfterSec;
}
public NotificationBadgeBuilder setExpireAfterSec(int expireAfterSec) {
this.expireAfterSec = expireAfterSec;
this.packetDirty = true;
return this;
}
public int getBackgroundColor() {
return backgroundColor;
}
public NotificationBadgeBuilder setBackgroundColor(int backgroundColor) {
this.backgroundColor = backgroundColor;
this.packetDirty = true;
return this;
}
public int getBodyTxtColorRGB() {
return bodyTxtColor;
}
public NotificationBadgeBuilder setBodyTxtColorRGB(int colorRGB) {
this.bodyTxtColor = colorRGB;
this.packetDirty = true;
return this;
}
public NotificationBadgeBuilder setBodyTxtColorRGB(int colorR, int colorG, int colorB) {
this.bodyTxtColor = (colorR << 16) | (colorG << 8) | colorB;
this.packetDirty = true;
return this;
}
public int getTitleTxtColorRGB() {
return titleTxtColor;
}
public NotificationBadgeBuilder setTitleTxtColorRGB(int colorRGB) {
this.titleTxtColor = colorRGB;
this.packetDirty = true;
return this;
}
public NotificationBadgeBuilder setTitleTxtColorRGB(int colorR, int colorG, int colorB) {
this.titleTxtColor = (colorR << 16) | (colorG << 8) | colorB;
this.packetDirty = true;
return this;
}
public int getSourceTxtColorRGB() {
return sourceTxtColor;
}
public NotificationBadgeBuilder setSourceTxtColorRGB(int colorRGB) {
this.sourceTxtColor = colorRGB;
this.packetDirty = true;
return this;
}
public NotificationBadgeBuilder setSourceTxtColorRGB(int colorR, int colorG, int colorB) {
this.sourceTxtColor = (colorR << 16) | (colorG << 8) | colorB;
this.packetDirty = true;
return this;
}
public Object clone() {
return new NotificationBadgeBuilder(this);
}
public SPacketNotifBadgeShowV4EAG buildPacket() {
if(packetDirty || packetCache == null) {
if(badgeUUID == null) {
badgeUUID = UUID.randomUUID();
}else if(badgeUUID.getMostSignificantBits() == 0l && badgeUUID.getLeastSignificantBits() == 0l) {
throw new IllegalStateException("Badge UUID cannot be 0!");
}
EnumBadgePriority internalPriority;
switch(priority) {
case LOW:
default:
internalPriority = EnumBadgePriority.LOW;
break;
case NORMAL:
internalPriority = EnumBadgePriority.NORMAL;
break;
case HIGHER:
internalPriority = EnumBadgePriority.HIGHER;
break;
case HIGHEST:
internalPriority = EnumBadgePriority.HIGHEST;
break;
}
String bodyComp = bodyComponent != null ? ComponentSerializer.toString(bodyComponent) : "";
if(bodyComp.length() > 32767) {
throw new IllegalStateException("Body component is longer than 32767 chars serialized!");
}
String titleComp = titleComponent != null ? ComponentSerializer.toString(titleComponent) : "";
if(titleComp.length() > 255) {
throw new IllegalStateException("Title component is longer than 255 chars serialized!");
}
String sourceComp = sourceComponent != null ? ComponentSerializer.toString(sourceComponent) : "";
if(sourceComp.length() > 255) {
throw new IllegalStateException("Body component is longer than 255 chars serialized!");
}
packetCache = new SPacketNotifBadgeShowV4EAG(badgeUUID.getMostSignificantBits(),
badgeUUID.getLeastSignificantBits(), bodyComp, titleComp, sourceComp, originalTimestampSec, silent,
internalPriority, mainIconUUID != null ? mainIconUUID.getMostSignificantBits() : 0l,
mainIconUUID != null ? mainIconUUID.getLeastSignificantBits() : 0l,
titleIconUUID != null ? titleIconUUID.getMostSignificantBits() : 0l,
titleIconUUID != null ? titleIconUUID.getLeastSignificantBits() : 0l, hideAfterSec, expireAfterSec,
backgroundColor, bodyTxtColor, titleTxtColor, sourceTxtColor);
packetDirty = false;
}
return packetCache;
}
}

View File

@ -1,93 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import java.net.InetAddress;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftClientBrandEvent extends Event implements Cancellable {
private final String clientBrand;
private final String clientVersion;
private final String origin;
private final int protocolVersion;
private final InetAddress remoteAddress;
private boolean cancelled;
private BaseComponent message;
public EaglercraftClientBrandEvent(String clientBrand, String clientVersion, String origin, int protocolVersion,
InetAddress remoteAddress) {
this.clientBrand = clientBrand;
this.clientVersion = clientVersion;
this.origin = origin;
this.protocolVersion = protocolVersion;
this.remoteAddress = remoteAddress;
}
public BaseComponent getMessage() {
return message;
}
public void setMessage(BaseComponent message) {
this.message = message;
}
public String getClientBrand() {
return clientBrand;
}
public String getClientVersion() {
return clientVersion;
}
public String getOrigin() {
return origin;
}
public int getProtocolVersion() {
return protocolVersion;
}
public InetAddress getRemoteAddress() {
return remoteAddress;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
public void setKickMessage(String message) {
this.cancelled = true;
this.message = new TextComponent(message);
}
public void setKickMessage(BaseComponent message) {
this.cancelled = true;
this.message = message;
}
}

View File

@ -1,207 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftHandleAuthCookieEvent extends Event {
public static enum AuthResponse {
ALLOW, DENY, REQUIRE_AUTH
}
private final EaglerListenerConfig listener;
private final InetAddress authRemoteAddress;
private final String authOrigin;
private final boolean enableCookies;
private final byte[] cookieData;
private final EaglercraftIsAuthRequiredEvent.AuthMethod eventAuthMethod;
private final String eventAuthMessage;
private final Object authAttachment;
private AuthResponse eventResponse;
private byte[] authUsername;
private String authProfileUsername;
private UUID authProfileUUID;
private String authRequestedServerRespose;
private String authDeniedMessage = "Bad Cookie!";
private String applyTexturesPropValue;
private String applyTexturesPropSignature;
private boolean overrideEaglerToVanillaSkins;
private Consumer<EaglercraftHandleAuthCookieEvent> continueThread;
private Runnable continueRunnable;
private volatile boolean hasContinue = false;
public EaglercraftHandleAuthCookieEvent(EaglerListenerConfig listener, InetAddress authRemoteAddress,
String authOrigin, byte[] authUsername, String authProfileUsername, UUID authProfileUUID,
boolean enableCookies, byte[] cookieData, EaglercraftIsAuthRequiredEvent.AuthMethod eventAuthMethod,
String eventAuthMessage, Object authAttachment, String authRequestedServerRespose,
Consumer<EaglercraftHandleAuthCookieEvent> continueThread) {
this.listener = listener;
this.authRemoteAddress = authRemoteAddress;
this.authOrigin = authOrigin;
this.authUsername = authUsername;
this.authProfileUsername = authProfileUsername;
this.authProfileUUID = authProfileUUID;
this.enableCookies = enableCookies;
this.cookieData = cookieData;
this.eventAuthMethod = eventAuthMethod;
this.eventAuthMessage = eventAuthMessage;
this.authAttachment = authAttachment;
this.authRequestedServerRespose = authRequestedServerRespose;
this.continueThread = continueThread;
}
public EaglerListenerConfig getListener() {
return listener;
}
public InetAddress getRemoteAddress() {
return authRemoteAddress;
}
public String getOriginHeader() {
return authOrigin;
}
public boolean getCookiesEnabled() {
return enableCookies;
}
public byte[] getCookieData() {
return cookieData;
}
public String getCookieDataString() {
return cookieData != null ? new String(cookieData, StandardCharsets.UTF_8) : null;
}
public byte[] getAuthUsername() {
return authUsername;
}
public String getProfileUsername() {
return authProfileUsername;
}
public void setProfileUsername(String username) {
this.authProfileUsername = username;
}
public UUID getProfileUUID() {
return authProfileUUID;
}
public void setProfileUUID(UUID uuid) {
this.authProfileUUID = uuid;
}
public EaglercraftIsAuthRequiredEvent.AuthMethod getAuthType() {
return eventAuthMethod;
}
public String getAuthMessage() {
return eventAuthMessage;
}
public <T> T getAuthAttachment() {
return (T)authAttachment;
}
public String getAuthRequestedServer() {
return authRequestedServerRespose;
}
public void setAuthRequestedServer(String server) {
this.authRequestedServerRespose = server;
}
public void setLoginAllowed() {
this.eventResponse = AuthResponse.ALLOW;
this.authDeniedMessage = null;
}
public void setLoginPasswordRequired() {
this.eventResponse = AuthResponse.REQUIRE_AUTH;
this.authDeniedMessage = null;
}
public void setLoginDenied(String message) {
this.eventResponse = AuthResponse.DENY;
this.authDeniedMessage = message;
}
public AuthResponse getLoginAllowed() {
return eventResponse;
}
public String getLoginDeniedMessage() {
return authDeniedMessage;
}
public Runnable makeAsyncContinue() {
if(continueRunnable == null) {
continueRunnable = new Runnable() {
@Override
public void run() {
if(!hasContinue) {
hasContinue = true;
continueThread.accept(EaglercraftHandleAuthCookieEvent.this);
}else {
throw new IllegalStateException("Thread was already continued from a different function! Auth plugin conflict?");
}
}
};
}
return continueRunnable;
}
public boolean isAsyncContinue() {
return continueRunnable != null;
}
public void doDirectContinue() {
continueThread.accept(this);
}
public void applyTexturesProperty(String value, String signature) {
applyTexturesPropValue = value;
applyTexturesPropSignature = signature;
}
public String getApplyTexturesPropertyValue() {
return applyTexturesPropValue;
}
public String getApplyTexturesPropertySignature() {
return applyTexturesPropSignature;
}
public void setOverrideEaglerToVanillaSkins(boolean overrideEaglerToVanillaSkins) {
this.overrideEaglerToVanillaSkins = overrideEaglerToVanillaSkins;
}
public boolean isOverrideEaglerToVanillaSkins() {
return overrideEaglerToVanillaSkins;
}
}

View File

@ -1,214 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftHandleAuthPasswordEvent extends Event {
public static enum AuthResponse {
ALLOW, DENY
}
private final EaglerListenerConfig listener;
private final InetAddress authRemoteAddress;
private final String authOrigin;
private final byte[] authUsername;
private final byte[] authSaltingData;
private final byte[] authPasswordData;
private final boolean enableCookies;
private final byte[] cookieData;
private final EaglercraftIsAuthRequiredEvent.AuthMethod eventAuthMethod;
private final String eventAuthMessage;
private final Object authAttachment;
private AuthResponse eventResponse;
private String authProfileUsername;
private UUID authProfileUUID;
private String authRequestedServerRespose;
private String authDeniedMessage = "Password Incorrect!";
private String applyTexturesPropValue;
private String applyTexturesPropSignature;
private boolean overrideEaglerToVanillaSkins;
private Consumer<EaglercraftHandleAuthPasswordEvent> continueThread;
private Runnable continueRunnable;
private volatile boolean hasContinue = false;
public EaglercraftHandleAuthPasswordEvent(EaglerListenerConfig listener, InetAddress authRemoteAddress,
String authOrigin, byte[] authUsername, byte[] authSaltingData, String authProfileUsername,
UUID authProfileUUID, byte[] authPasswordData, boolean enableCookies, byte[] cookieData,
EaglercraftIsAuthRequiredEvent.AuthMethod eventAuthMethod, String eventAuthMessage, Object authAttachment,
String authRequestedServerRespose, Consumer<EaglercraftHandleAuthPasswordEvent> continueThread) {
this.listener = listener;
this.authRemoteAddress = authRemoteAddress;
this.authOrigin = authOrigin;
this.authUsername = authUsername;
this.authSaltingData = authSaltingData;
this.authProfileUsername = authProfileUsername;
this.authProfileUUID = authProfileUUID;
this.authPasswordData = authPasswordData;
this.enableCookies = enableCookies;
this.cookieData = cookieData;
this.eventAuthMethod = eventAuthMethod;
this.eventAuthMessage = eventAuthMessage;
this.authAttachment = authAttachment;
this.authRequestedServerRespose = authRequestedServerRespose;
this.continueThread = continueThread;
}
public EaglerListenerConfig getListener() {
return listener;
}
public InetAddress getRemoteAddress() {
return authRemoteAddress;
}
public String getOriginHeader() {
return authOrigin;
}
public byte[] getAuthUsername() {
return authUsername;
}
public byte[] getAuthSaltingData() {
return authSaltingData;
}
public boolean getCookiesEnabled() {
return enableCookies;
}
public byte[] getCookieData() {
return cookieData;
}
public String getCookieDataString() {
return cookieData != null ? new String(cookieData, StandardCharsets.UTF_8) : null;
}
public String getProfileUsername() {
return authProfileUsername;
}
public void setProfileUsername(String username) {
this.authProfileUsername = username;
}
public UUID getProfileUUID() {
return authProfileUUID;
}
public void setProfileUUID(UUID uuid) {
this.authProfileUUID = uuid;
}
public byte[] getAuthPasswordDataResponse() {
return authPasswordData;
}
public EaglercraftIsAuthRequiredEvent.AuthMethod getAuthType() {
return eventAuthMethod;
}
public String getAuthMessage() {
return eventAuthMessage;
}
public <T> T getAuthAttachment() {
return (T)authAttachment;
}
public String getAuthRequestedServer() {
return authRequestedServerRespose;
}
public void setAuthRequestedServer(String server) {
this.authRequestedServerRespose = server;
}
public void setLoginAllowed() {
this.eventResponse = AuthResponse.ALLOW;
this.authDeniedMessage = null;
}
public void setLoginDenied(String message) {
this.eventResponse = AuthResponse.DENY;
this.authDeniedMessage = message;
}
public AuthResponse getLoginAllowed() {
return eventResponse;
}
public String getLoginDeniedMessage() {
return authDeniedMessage;
}
public Runnable makeAsyncContinue() {
if(continueRunnable == null) {
continueRunnable = new Runnable() {
@Override
public void run() {
if(!hasContinue) {
hasContinue = true;
continueThread.accept(EaglercraftHandleAuthPasswordEvent.this);
}else {
throw new IllegalStateException("Thread was already continued from a different function! Auth plugin conflict?");
}
}
};
}
return continueRunnable;
}
public boolean isAsyncContinue() {
return continueRunnable != null;
}
public void doDirectContinue() {
continueThread.accept(this);
}
public void applyTexturesProperty(String value, String signature) {
applyTexturesPropValue = value;
applyTexturesPropSignature = signature;
}
public String getApplyTexturesPropertyValue() {
return applyTexturesPropValue;
}
public String getApplyTexturesPropertySignature() {
return applyTexturesPropSignature;
}
public void setOverrideEaglerToVanillaSkins(boolean overrideEaglerToVanillaSkins) {
this.overrideEaglerToVanillaSkins = overrideEaglerToVanillaSkins;
}
public boolean isOverrideEaglerToVanillaSkins() {
return overrideEaglerToVanillaSkins;
}
}

View File

@ -1,168 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import java.net.InetAddress;
import java.util.function.Consumer;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftIsAuthRequiredEvent extends Event {
public static enum AuthResponse {
SKIP, REQUIRE, DENY
}
public static enum AuthMethod {
PLAINTEXT, EAGLER_SHA256, AUTHME_SHA256
}
public EaglercraftIsAuthRequiredEvent(EaglerListenerConfig listener, InetAddress authRemoteAddress,
String authOrigin, boolean wantsAuth, byte[] authUsername,
Consumer<EaglercraftIsAuthRequiredEvent> continueThread) {
this.listener = listener;
this.authRemoteAddress = authRemoteAddress;
this.authOrigin = authOrigin;
this.wantsAuth = wantsAuth;
this.authUsername = authUsername;
this.continueThread = continueThread;
}
private final EaglerListenerConfig listener;
private AuthResponse authResponse;
private final InetAddress authRemoteAddress;
private final String authOrigin;
private final boolean wantsAuth;
private final byte[] authUsername;
private byte[] authSaltingData;
private AuthMethod eventAuthMethod = null;
private String eventAuthMessage = "enter the code:";
private String kickUserMessage = "Login Denied";
private Object authAttachment;
private boolean enableCookieAuth;
private Consumer<EaglercraftIsAuthRequiredEvent> continueThread;
private Runnable continueRunnable;
private volatile boolean hasContinue = false;
public EaglerListenerConfig getListener() {
return listener;
}
public InetAddress getRemoteAddress() {
return authRemoteAddress;
}
public String getOriginHeader() {
return authOrigin;
}
public boolean isClientSolicitingPasscode() {
return wantsAuth;
}
public byte[] getAuthUsername() {
return authUsername;
}
public byte[] getSaltingData() {
return authSaltingData;
}
public void setSaltingData(byte[] saltingData) {
authSaltingData = saltingData;
}
public AuthMethod getUseAuthType() {
return eventAuthMethod;
}
public void setUseAuthMethod(AuthMethod authMethod) {
this.eventAuthMethod = authMethod;
}
public AuthResponse getAuthRequired() {
return authResponse;
}
public void setAuthRequired(AuthResponse required) {
this.authResponse = required;
}
public String getAuthMessage() {
return eventAuthMessage;
}
public void setAuthMessage(String eventAuthMessage) {
this.eventAuthMessage = eventAuthMessage;
}
public <T> T getAuthAttachment() {
return (T)authAttachment;
}
public void setAuthAttachment(Object authAttachment) {
this.authAttachment = authAttachment;
}
public boolean getEnableCookieAuth() {
return enableCookieAuth;
}
public void setEnableCookieAuth(boolean enable) {
this.enableCookieAuth = enable;
}
public boolean shouldKickUser() {
return authResponse == null || authResponse == AuthResponse.DENY;
}
public String getKickMessage() {
return kickUserMessage;
}
public void kickUser(String message) {
authResponse = AuthResponse.DENY;
kickUserMessage = message;
}
public Runnable makeAsyncContinue() {
if(continueRunnable == null) {
continueRunnable = new Runnable() {
@Override
public void run() {
if(!hasContinue) {
hasContinue = true;
continueThread.accept(EaglercraftIsAuthRequiredEvent.this);
}else {
throw new IllegalStateException("Thread was already continued from a different function! Auth plugin conflict?");
}
}
};
}
return continueRunnable;
}
public boolean isAsyncContinue() {
return continueRunnable != null;
}
public void doDirectContinue() {
continueThread.accept(this);
}
}

View File

@ -1,49 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import java.net.InetAddress;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query.MOTDConnection;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftMOTDEvent extends Event {
protected final MOTDConnection connection;
public EaglercraftMOTDEvent(MOTDConnection connection) {
this.connection = connection;
}
public InetAddress getRemoteAddress() {
return connection.getAddress();
}
public EaglerListenerConfig getListener() {
return connection.getListener();
}
public String getAccept() {
return connection.getAccept();
}
public MOTDConnection getConnection() {
return connection;
}
}

View File

@ -1,74 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import java.util.UUID;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftRegisterCapeEvent extends Event {
private final Object authAttachment;
private final String username;
private final UUID uuid;
private byte[] customTex = null;
public EaglercraftRegisterCapeEvent(String username, UUID uuid, Object authAttachment) {
this.username = username;
this.uuid = uuid;
this.authAttachment = authAttachment;
}
public String getUsername() {
return username;
}
public UUID getUuid() {
return uuid;
}
public void setForceUsePreset(int p) {
customTex = new byte[5];
customTex[0] = (byte)1;
customTex[1] = (byte)(p >>> 24);
customTex[2] = (byte)(p >>> 16);
customTex[3] = (byte)(p >>> 8);
customTex[4] = (byte)(p & 0xFF);
}
/**
* @param tex raw 32x32 pixel RGBA texture (4096 bytes long), see capes in "sources/resources/assets/eagler/capes"
*/
public void setForceUseCustom(byte[] tex) {
customTex = new byte[1174];
customTex[0] = (byte)2;
EaglerXBungeeAPIHelper.convertCape32x32RGBAto23x17RGB(tex, 0, customTex, 1);
}
public void setForceUseCustomByPacket(byte[] packet) {
customTex = packet;
}
public byte[] getForceSetUseCustomPacket() {
return customTex;
}
public <T> T getAuthAttachment() {
return (T)authAttachment;
}
}

View File

@ -1,104 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import java.util.UUID;
import net.md_5.bungee.api.plugin.Event;
import net.md_5.bungee.protocol.Property;
public class EaglercraftRegisterSkinEvent extends Event {
private final Object authAttachment;
private final String username;
private final UUID uuid;
private Property useMojangProfileProperty = null;
private boolean useLoginResultTextures = false;
private byte[] customTex = null;
public EaglercraftRegisterSkinEvent(String username, UUID uuid, Object authAttachment) {
this.username = username;
this.uuid = uuid;
this.authAttachment = authAttachment;
}
public void setForceUseMojangProfileProperty(Property prop) {
useMojangProfileProperty = prop;
useLoginResultTextures = false;
customTex = null;
}
public void setForceUseLoginResultObjectTextures(boolean b) {
useMojangProfileProperty = null;
useLoginResultTextures = b;
customTex = null;
}
public void setForceUsePreset(int p) {
useMojangProfileProperty = null;
useLoginResultTextures = false;
customTex = new byte[5];
customTex[0] = (byte)1;
customTex[1] = (byte)(p >>> 24);
customTex[2] = (byte)(p >>> 16);
customTex[3] = (byte)(p >>> 8);
customTex[4] = (byte)(p & 0xFF);
}
/**
* @param tex raw 64x64 pixel RGBA texture (16384 bytes long)
*/
public void setForceUseCustom(int model, byte[] tex) {
useMojangProfileProperty = null;
useLoginResultTextures = false;
customTex = new byte[2 + tex.length];
customTex[0] = (byte)2;
customTex[1] = (byte)model;
System.arraycopy(tex, 0, customTex, 2, tex.length);
}
public void setForceUseCustomByPacket(byte[] packet) {
useMojangProfileProperty = null;
useLoginResultTextures = false;
customTex = packet;
}
public String getUsername() {
return username;
}
public UUID getUuid() {
return uuid;
}
public Property getForceUseMojangProfileProperty() {
return useMojangProfileProperty;
}
public boolean getForceUseLoginResultObjectTextures() {
return useLoginResultTextures;
}
public byte[] getForceSetUseCustomPacket() {
return customTex;
}
public <T> T getAuthAttachment() {
return (T)authAttachment;
}
}

View File

@ -1,92 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.query.RevokeSessionQueryHandler;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftRevokeSessionQueryEvent extends Event {
private final InetAddress remoteAddress;
private final String origin;
private final byte[] cookieData;
private final RevokeSessionQueryHandler queryHandler;
private EnumSessionRevokeStatus revokeStatus;
private boolean shouldDeleteCookie;
public static enum EnumSessionRevokeStatus {
SUCCESS("ok", -1), FAILED_NOT_SUPPORTED("error", 1), FAILED_NOT_ALLOWED("error", 2),
FAILED_NOT_FOUND("error", 3), FAILED_SERVER_ERROR("error", 4);
public final String status;
public final int code;
private EnumSessionRevokeStatus(String status, int code) {
this.status = status;
this.code = code;
}
}
public EaglercraftRevokeSessionQueryEvent(InetAddress remoteAddress, String origin, byte[] cookieData,
RevokeSessionQueryHandler queryHandler) {
this.remoteAddress = remoteAddress;
this.origin = origin;
this.cookieData = cookieData;
this.queryHandler = queryHandler;
this.revokeStatus = EnumSessionRevokeStatus.FAILED_NOT_SUPPORTED;
this.shouldDeleteCookie = false;
}
public InetAddress getRemoteAddress() {
return remoteAddress;
}
public String getOrigin() {
return origin;
}
public byte[] getCookieData() {
return cookieData;
}
public String getCookieDataString() {
return new String(cookieData, StandardCharsets.UTF_8);
}
public RevokeSessionQueryHandler getQuery() {
return queryHandler;
}
public void setResultStatus(EnumSessionRevokeStatus revokeStatus) {
this.revokeStatus = revokeStatus;
}
public EnumSessionRevokeStatus getResultStatus() {
return revokeStatus;
}
public boolean getShouldDeleteCookie() {
return shouldDeleteCookie;
}
public void setShouldDeleteCookie(boolean b) {
this.shouldDeleteCookie = b;
}
}

View File

@ -1,65 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftVoiceStatusChangeEvent extends Event {
public static enum EnumVoiceState {
SERVER_DISABLE, DISABLED, ENABLED;
}
private final ProxiedPlayer playerObj;
private final EaglerListenerConfig listener;
private final EaglerInitialHandler eaglerHandler;
private final EnumVoiceState voiceStateOld;
private final EnumVoiceState voiceStateNew;
public EaglercraftVoiceStatusChangeEvent(ProxiedPlayer playerObj, EaglerListenerConfig listener,
EaglerInitialHandler eaglerHandler, EnumVoiceState voiceStateOld, EnumVoiceState voiceStateNew) {
this.playerObj = playerObj;
this.listener = listener;
this.eaglerHandler = eaglerHandler;
this.voiceStateOld = voiceStateOld;
this.voiceStateNew = voiceStateNew;
}
public ProxiedPlayer getPlayerObj() {
return playerObj;
}
public EaglerListenerConfig getListener() {
return listener;
}
public EaglerInitialHandler getEaglerHandler() {
return eaglerHandler;
}
public EnumVoiceState getVoiceStateOld() {
return voiceStateOld;
}
public EnumVoiceState getVoiceStateNew() {
return voiceStateNew;
}
}

View File

@ -1,71 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import io.netty.channel.Channel;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.md_5.bungee.api.plugin.Cancellable;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftWebSocketOpenEvent extends Event implements Cancellable {
private final Channel channel;
private final EaglerListenerConfig listener;
private final String realIP;
private final String origin;
private final String userAgent;
private boolean cancelled = false;
public EaglercraftWebSocketOpenEvent(Channel channel, EaglerListenerConfig listener, String realIP, String origin, String userAgent) {
this.channel = channel;
this.listener = listener;
this.realIP = realIP;
this.origin = origin;
this.userAgent = userAgent;
}
@Override
public boolean isCancelled() {
return cancelled;
}
@Override
public void setCancelled(boolean var1) {
cancelled = var1;
}
public Channel getChannel() {
return channel;
}
public EaglerListenerConfig getListener() {
return listener;
}
public String getRealIP() {
return realIP;
}
public String getOrigin() {
return origin;
}
public String getUserAgent() {
return userAgent;
}
}

View File

@ -1,57 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftWebViewChannelEvent extends Event {
public static enum EventType {
CHANNEL_OPEN, CHANNEL_CLOSE;
}
private final ProxiedPlayer player;
private final EaglerListenerConfig listener;
private final String channel;
private final EventType type;
public EaglercraftWebViewChannelEvent(ProxiedPlayer player, EaglerListenerConfig listener, String channel, EventType type) {
this.player = player;
this.listener = listener;
this.channel = channel;
this.type = type;
}
public ProxiedPlayer getPlayer() {
return player;
}
public EaglerListenerConfig getListener() {
return listener;
}
public String getChannel() {
return channel;
}
public EventType getType() {
return type;
}
}

View File

@ -1,119 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event;
import java.nio.charset.StandardCharsets;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.client.CPacketWebViewMessageV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketWebViewMessageV4EAG;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Event;
public class EaglercraftWebViewMessageEvent extends Event {
public static enum MessageType {
STRING(SPacketWebViewMessageV4EAG.TYPE_STRING), BINARY(SPacketWebViewMessageV4EAG.TYPE_BINARY);
private final int id;
private MessageType(int id) {
this.id = id;
}
private static MessageType fromId(int id) {
switch(id) {
case CPacketWebViewMessageV4EAG.TYPE_STRING:
return STRING;
default:
case CPacketWebViewMessageV4EAG.TYPE_BINARY:
return BINARY;
}
}
}
private final ProxiedPlayer player;
private final EaglerListenerConfig listener;
private final String currentChannel;
private final EaglerInitialHandler eaglerHandler;
private final MessageType type;
private final byte[] data;
private String asString;
public EaglercraftWebViewMessageEvent(ProxiedPlayer player, EaglerListenerConfig listener, String currentChannel, MessageType type, byte[] data) {
this.player = player;
this.listener = listener;
this.currentChannel = currentChannel;
this.eaglerHandler = EaglerXBungeeAPIHelper.getEaglerHandle(player);
this.type = type;
this.data = data;
}
public EaglercraftWebViewMessageEvent(ProxiedPlayer player, EaglerListenerConfig listener, String currentChannel, CPacketWebViewMessageV4EAG packet) {
this.player = player;
this.listener = listener;
this.currentChannel = currentChannel;
this.eaglerHandler = EaglerXBungeeAPIHelper.getEaglerHandle(player);
this.type = MessageType.fromId(packet.type);
this.data = packet.data;
}
public ProxiedPlayer getPlayer() {
return player;
}
public EaglerListenerConfig getListener() {
return listener;
}
public EaglerInitialHandler getEaglerHandle() {
return eaglerHandler;
}
public void sendResponse(MessageType type, byte[] data) {
eaglerHandler.sendEaglerMessage(new SPacketWebViewMessageV4EAG(type.id, data));
}
public void sendResponse(String str) {
sendResponse(MessageType.STRING, str.getBytes(StandardCharsets.UTF_8));
}
public void sendResponse(byte[] data) {
sendResponse(MessageType.BINARY, data);
}
public MessageType getType() {
return type;
}
public byte[] getAsBinary() {
return data;
}
public String getAsString() {
if(asString == null) {
asString = new String(data, StandardCharsets.UTF_8);
}
return asString;
}
public String getChannelName() {
return currentChannel;
}
}

View File

@ -1,32 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.HttpServerQueryHandler;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.query.QueryManager;
public abstract class EaglerQueryHandler extends HttpServerQueryHandler {
public static void registerQueryType(String name, Class<? extends EaglerQueryHandler> clazz) {
QueryManager.registerQueryType(name, clazz);
}
public static void unregisterQueryType(String name) {
QueryManager.unregisterQueryType(name);
}
}

View File

@ -1,62 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query;
import com.google.gson.JsonObject;
public abstract class EaglerQuerySimpleHandler extends EaglerQueryHandler {
@Override
protected void processString(String str) {
throw new UnexpectedDataException();
}
@Override
protected void processJson(JsonObject obj) {
throw new UnexpectedDataException();
}
@Override
protected void processBytes(byte[] bytes) {
throw new UnexpectedDataException();
}
@Override
protected void acceptText() {
throw new UnsupportedOperationException("EaglerQuerySimpleHandler does not support duplex");
}
@Override
protected void acceptText(boolean bool) {
throw new UnsupportedOperationException("EaglerQuerySimpleHandler does not support duplex");
}
@Override
protected void acceptBinary() {
throw new UnsupportedOperationException("EaglerQuerySimpleHandler does not support duplex");
}
@Override
protected void acceptBinary(boolean bool) {
throw new UnsupportedOperationException("EaglerQuerySimpleHandler does not support duplex");
}
@Override
protected void closed() {
}
}

View File

@ -1,58 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.query;
import java.net.InetAddress;
import java.util.List;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
public interface MOTDConnection {
boolean isClosed();
void close();
String getAccept();
InetAddress getAddress();
EaglerListenerConfig getListener();
long getConnectionTimestamp();
public default long getConnectionAge() {
return EaglerXBungeeAPIHelper.steadyTimeMillis() - getConnectionTimestamp();
}
void sendToUser();
void setKeepAlive(boolean enable);
String getLine1();
String getLine2();
List<String> getPlayerList();
int[] getBitmap();
int getOnlinePlayers();
int getMaxPlayers();
String getSubType();
void setLine1(String p);
void setLine2(String p);
void setPlayerList(List<String> p);
void setPlayerList(String... p);
void setBitmap(int[] p);
void setOnlinePlayers(int i);
void setMaxPlayers(int i);
}

View File

@ -1,139 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
public class AuthLoadingCache<K, V> {
private static class CacheEntry<V> {
private long lastHit;
private V instance;
private CacheEntry(V instance) {
this.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis();
this.instance = instance;
}
}
public static interface CacheLoader<K, V> {
V load(K key);
}
public static interface CacheVisitor<K, V> {
boolean shouldEvict(K key, V value);
}
private final ReadWriteLock cacheMapLock;
private final Map<K, CacheEntry<V>> cacheMap;
private final CacheLoader<K, V> provider;
private final long cacheTTL;
private long cacheTimer;
public AuthLoadingCache(CacheLoader<K, V> provider, long cacheTTL) {
this.cacheMapLock = new ReentrantReadWriteLock();
this.cacheMap = new HashMap<>();
this.provider = provider;
this.cacheTTL = cacheTTL;
}
public V get(K key) {
CacheEntry<V> etr;
cacheMapLock.readLock().lock();
try {
etr = cacheMap.get(key);
}finally {
cacheMapLock.readLock().unlock();
}
if(etr == null) {
cacheMapLock.writeLock().lock();
V loaded = provider.load(key);
try {
cacheMap.put(key, new CacheEntry<>(loaded));
}finally {
cacheMapLock.writeLock().unlock();
}
return loaded;
}else {
etr.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis();
return etr.instance;
}
}
public void evict(K key) {
cacheMapLock.writeLock().lock();
try {
cacheMap.remove(key);
}finally {
cacheMapLock.writeLock().unlock();
}
}
public void evictAll(CacheVisitor<K, V> visitor) {
cacheMapLock.writeLock().lock();
try {
Iterator<Entry<K,CacheEntry<V>>> itr = cacheMap.entrySet().iterator();
while(itr.hasNext()) {
Entry<K,CacheEntry<V>> etr = itr.next();
if(visitor.shouldEvict(etr.getKey(), etr.getValue().instance)) {
itr.remove();
}
}
}finally {
cacheMapLock.writeLock().unlock();
}
}
public void tick() {
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
if(millis - cacheTimer > (cacheTTL / 2L)) {
cacheTimer = millis;
cacheMapLock.writeLock().lock();
try {
Iterator<CacheEntry<V>> mapItr = cacheMap.values().iterator();
while(mapItr.hasNext()) {
CacheEntry<V> etr = mapItr.next();
if(millis - etr.lastHit > cacheTTL) {
mapItr.remove();
}
}
}finally {
cacheMapLock.writeLock().unlock();
}
}
}
public void flush() {
cacheMapLock.writeLock().lock();
try {
cacheMap.clear();
}finally {
cacheMapLock.writeLock().unlock();
}
}
}

View File

@ -1,682 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Properties;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Level;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.ArrayUtils;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftHandleAuthPasswordEvent;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftIsAuthRequiredEvent;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftIsAuthRequiredEvent.AuthMethod;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.event.EaglercraftIsAuthRequiredEvent.AuthResponse;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerAuthConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.sqlite.EaglerDrivers;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.protocol.Property;
public class DefaultAuthSystem {
public static class AuthSystemException extends RuntimeException {
public AuthSystemException() {
}
public AuthSystemException(String message, Throwable cause) {
super(message, cause);
}
public AuthSystemException(String message) {
super(message);
}
public AuthSystemException(Throwable cause) {
super(cause);
}
}
protected final String uri;
protected final Connection databaseConnection;
protected final String passwordPromptScreenText;
protected final String wrongPasswordScreenText;
protected final String notRegisteredScreenText;
protected final String eaglerCommandName;
protected final String useRegisterCommandText;
protected final String useChangeCommandText;
protected final String commandSuccessText;
protected final String lastEaglerLoginMessage;
protected final String tooManyRegistrationsMessage;
protected final String needVanillaToRegisterMessage;
protected final boolean overrideEaglerToVanillaSkins;
protected final int maxRegistrationsPerIP;
protected final SecureRandom secureRandom;
public static DefaultAuthSystem initializeAuthSystem(EaglerAuthConfig config) throws AuthSystemException {
String databaseURI = config.getDatabaseURI();
Connection conn;
try {
conn = EaglerDrivers.connectToDatabase(databaseURI, config.getDriverClass(), config.getDriverPath(), new Properties());
if(conn == null) {
throw new IllegalStateException("Connection is null");
}
}catch(Throwable t) {
throw new AuthSystemException("Could not initialize '" + databaseURI + "'!", t);
}
EaglerXBungee.logger().info("Connected to database: " + databaseURI);
try {
try(Statement stmt = conn.createStatement()) {
stmt.execute("CREATE TABLE IF NOT EXISTS \"eaglercraft_accounts\" ("
+ "\"Version\" TINYINT NOT NULL,"
+ "\"MojangUUID\" TEXT(32) NOT NULL,"
+ "\"MojangUsername\" TEXT(16) NOT NULL,"
+ "\"HashBase\" BLOB NOT NULL,"
+ "\"HashSalt\" BLOB NOT NULL,"
+ "\"MojangTextures\" BLOB,"
+ "\"Registered\" DATETIME NOT NULL,"
+ "\"RegisteredIP\" VARCHAR(42) NOT NULL,"
+ "\"LastLogin\" DATETIME,"
+ "\"LastLoginIP\" VARCHAR(42),"
+ "PRIMARY KEY(\"MojangUUID\"))");
stmt.execute("CREATE UNIQUE INDEX IF NOT EXISTS \"MojangUsername\" ON "
+ "\"eaglercraft_accounts\" (\"MojangUsername\")");
}
return new DefaultAuthSystem(databaseURI, conn, config.getPasswordPromptScreenText(),
config.getWrongPasswordScreenText(), config.getNotRegisteredScreenText(),
config.getEaglerCommandName(), config.getUseRegisterCommandText(), config.getUseChangeCommandText(),
config.getCommandSuccessText(), config.getLastEaglerLoginMessage(),
config.getTooManyRegistrationsMessage(), config.getNeedVanillaToRegisterMessage(),
config.getOverrideEaglerToVanillaSkins(), config.getMaxRegistrationsPerIP());
}catch(AuthSystemException ex) {
try {
conn.close();
}catch(SQLException exx) {
}
throw ex;
}catch(Throwable t) {
try {
conn.close();
}catch(SQLException exx) {
}
throw new AuthSystemException("Could not initialize '" + databaseURI + "'!", t);
}
}
protected final PreparedStatement registerUser;
protected final PreparedStatement isRegisteredUser;
protected final PreparedStatement pruneUsers;
protected final PreparedStatement updatePassword;
protected final PreparedStatement updateMojangUsername;
protected final PreparedStatement getRegistrationsOnIP;
protected final PreparedStatement checkRegistrationByUUID;
protected final PreparedStatement checkRegistrationByName;
protected final PreparedStatement setLastLogin;
protected final PreparedStatement updateTextures;
protected class AccountLoader implements AuthLoadingCache.CacheLoader<String, CachedAccountInfo> {
@Override
public CachedAccountInfo load(String key) {
try {
CachedAccountInfo cachedInfo = null;
synchronized(checkRegistrationByName) {
checkRegistrationByName.setString(1, key);
try(ResultSet res = checkRegistrationByName.executeQuery()) {
if (res.next()) {
cachedInfo = new CachedAccountInfo(res.getInt(1), parseMojangUUID(res.getString(2)), key,
res.getBytes(3), res.getBytes(4), res.getBytes(5), res.getDate(6), res.getString(7),
res.getDate(8), res.getString(9));
}
}
}
return cachedInfo;
}catch(SQLException ex) {
throw new AuthException("Failed to query database!", ex);
}
}
}
protected class CachedAccountInfo {
protected int version;
protected UUID mojangUUID;
protected String mojangUsername;
protected byte[] texturesProperty;
protected byte[] hashBase;
protected byte[] hashSalt;
protected long registered;
protected String registeredIP;
protected long lastLogin;
protected String lastLoginIP;
protected CachedAccountInfo(int version, UUID mojangUUID, String mojangUsername, byte[] texturesProperty,
byte[] hashBase, byte[] hashSalt, Date registered, String registeredIP, Date lastLogin,
String lastLoginIP) {
this(version, mojangUUID, mojangUsername, texturesProperty, hashBase, hashSalt,
registered == null ? 0l : registered.getTime(), registeredIP,
lastLogin == null ? 0l : lastLogin.getTime(), lastLoginIP);
}
protected CachedAccountInfo(int version, UUID mojangUUID, String mojangUsername, byte[] texturesProperty,
byte[] hashBase, byte[] hashSalt, long registered, String registeredIP, long lastLogin,
String lastLoginIP) {
this.version = version;
this.mojangUUID = mojangUUID;
this.mojangUsername = mojangUsername;
this.texturesProperty = texturesProperty;
this.hashBase = hashBase;
this.hashSalt = hashSalt;
this.registered = registered;
this.registeredIP = registeredIP;
this.lastLogin = lastLogin;
this.lastLoginIP = lastLoginIP;
}
}
protected final AuthLoadingCache<String, CachedAccountInfo> authLoadingCache;
protected DefaultAuthSystem(String uri, Connection databaseConnection, String passwordPromptScreenText,
String wrongPasswordScreenText, String notRegisteredScreenText, String eaglerCommandName,
String useRegisterCommandText, String useChangeCommandText, String commandSuccessText,
String lastEaglerLoginMessage, String tooManyRegistrationsMessage, String needVanillaToRegisterMessage,
boolean overrideEaglerToVanillaSkins, int maxRegistrationsPerIP) throws SQLException {
this.uri = uri;
this.databaseConnection = databaseConnection;
this.passwordPromptScreenText = passwordPromptScreenText;
this.wrongPasswordScreenText = wrongPasswordScreenText;
this.notRegisteredScreenText = notRegisteredScreenText;
this.eaglerCommandName = eaglerCommandName;
this.useRegisterCommandText = useRegisterCommandText;
this.useChangeCommandText = useChangeCommandText;
this.commandSuccessText = commandSuccessText;
this.lastEaglerLoginMessage = lastEaglerLoginMessage;
this.tooManyRegistrationsMessage = tooManyRegistrationsMessage;
this.needVanillaToRegisterMessage = needVanillaToRegisterMessage;
this.overrideEaglerToVanillaSkins = overrideEaglerToVanillaSkins;
this.maxRegistrationsPerIP = maxRegistrationsPerIP;
this.registerUser = databaseConnection.prepareStatement("INSERT INTO eaglercraft_accounts (Version, MojangUUID, MojangUsername, MojangTextures, HashBase, HashSalt, Registered, RegisteredIP) VALUES(?, ?, ?, ?, ?, ?, ?, ?)");
this.isRegisteredUser = databaseConnection.prepareStatement("SELECT COUNT(MojangUUID) AS total_accounts FROM eaglercraft_accounts WHERE MojangUUID = ?");
this.pruneUsers = databaseConnection.prepareStatement("DELETE FROM eaglercraft_accounts WHERE LastLogin < ?");
this.updatePassword = databaseConnection.prepareStatement("UPDATE eaglercraft_accounts SET HashBase = ?, HashSalt = ? WHERE MojangUUID = ?");
this.updateMojangUsername = databaseConnection.prepareStatement("UPDATE eaglercraft_accounts SET MojangUsername = ? WHERE MojangUUID = ?");
this.getRegistrationsOnIP = databaseConnection.prepareStatement("SELECT COUNT(MojangUUID) AS total_accounts FROM eaglercraft_accounts WHERE RegisteredIP = ?");
this.checkRegistrationByUUID = databaseConnection.prepareStatement("SELECT Version, MojangUsername, LastLogin, LastLoginIP FROM eaglercraft_accounts WHERE MojangUUID = ?");
this.checkRegistrationByName = databaseConnection.prepareStatement("SELECT Version, MojangUUID, MojangTextures, HashBase, HashSalt, Registered, RegisteredIP, LastLogin, LastLoginIP FROM eaglercraft_accounts WHERE MojangUsername = ?");
this.setLastLogin = databaseConnection.prepareStatement("UPDATE eaglercraft_accounts SET LastLogin = ?, LastLoginIP = ? WHERE MojangUUID = ?");
this.updateTextures = databaseConnection.prepareStatement("UPDATE eaglercraft_accounts SET MojangTextures = ? WHERE MojangUUID = ?");
this.authLoadingCache = new AuthLoadingCache<>(new AccountLoader(), 120000l);
this.secureRandom = new SecureRandom();
}
public void handleIsAuthRequiredEvent(EaglercraftIsAuthRequiredEvent event) {
String username = new String(event.getAuthUsername(), StandardCharsets.US_ASCII);
String usrs = username.toString();
if(!usrs.equals(usrs.replaceAll("[^A-Za-z0-9_]", "_").trim())) {
event.kickUser("Invalid characters in username");
return;
}
if(username.length() < 3) {
event.kickUser("Username must be at least 3 characters");
return;
}
if(username.length() > 16) {
event.kickUser("Username must be under 16 characters");
return;
}
CachedAccountInfo info = authLoadingCache.get(username);
if(info == null) {
event.kickUser(notRegisteredScreenText);
return;
}
event.setAuthAttachment(info);
event.setAuthRequired(AuthResponse.REQUIRE);
event.setAuthMessage(passwordPromptScreenText);
event.setUseAuthMethod(AuthMethod.EAGLER_SHA256);
byte[] randomBytes = new byte[32];
Random rng;
synchronized(secureRandom) {
rng = new Random(secureRandom.nextLong());
}
rng.nextBytes(randomBytes);
byte[] saltingData = new byte[64];
System.arraycopy(info.hashSalt, 0, saltingData, 0, 32);
System.arraycopy(randomBytes, 0, saltingData, 32, 32);
event.setSaltingData(saltingData);
}
public void handleAuthPasswordEvent(EaglercraftHandleAuthPasswordEvent event) {
CachedAccountInfo info = event.getAuthAttachment();
if(info == null) {
event.setLoginDenied(notRegisteredScreenText);
return;
}
byte[] responseHash = event.getAuthPasswordDataResponse();
if(responseHash.length != 32) {
event.setLoginDenied("Wrong number of bits in checksum!");
return;
}
byte[] saltingData = event.getAuthSaltingData();
SHA256Digest digest = new SHA256Digest();
digest.update(info.hashBase, 0, 32);
digest.update(saltingData, 32, 32);
digest.update(HashUtils.EAGLER_SHA256_SALT_BASE, 0, 32);
byte[] hashed = new byte[32];
digest.doFinal(hashed, 0);
if(!Arrays.equals(hashed, responseHash)) {
event.setLoginDenied(wrongPasswordScreenText);
EaglerXBungee.logger().warning("User \"" + info.mojangUsername + "\" entered the wrong password while logging in from: " + event.getRemoteAddress().getHostAddress());
return;
}
try {
synchronized(setLastLogin) {
setLastLogin.setDate(1, new Date(System.currentTimeMillis()));
setLastLogin.setString(2, event.getRemoteAddress().getHostAddress());
setLastLogin.setString(3, getMojangUUID(info.mojangUUID));
if(setLastLogin.executeUpdate() == 0) {
throw new SQLException("Query did not alter the database");
}
}
}catch(SQLException ex) {
EaglerXBungee.logger().log(Level.SEVERE, "Could not update last login for \"" + info.mojangUUID.toString() + "\"", ex);
}
event.setLoginAllowed();
event.setProfileUsername(info.mojangUsername);
event.setProfileUUID(info.mojangUUID);
byte[] texturesProp = info.texturesProperty;
if(texturesProp != null) {
try {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(texturesProp));
int valueLen = dis.readInt();
int sigLen = dis.readInt();
byte[] valueBytes = new byte[valueLen];
dis.read(valueBytes);
String valueB64 = Base64.encodeBase64String(valueBytes);
String sigB64 = null;
if(sigLen > 0) {
valueBytes = new byte[sigLen];
dis.read(valueBytes);
sigB64 = Base64.encodeBase64String(valueBytes);
}
event.applyTexturesProperty(valueB64, sigB64);
event.setOverrideEaglerToVanillaSkins(overrideEaglerToVanillaSkins);
}catch(IOException ex) {
}
}
}
public void processSetPassword(ProxiedPlayer player, String password) throws TooManyRegisteredOnIPException, AuthException {
PendingConnection conn = player.getPendingConnection();
if(conn instanceof EaglerInitialHandler) {
throw new AuthException("Cannot register from an eaglercraft account!");
}else if(!conn.isOnlineMode()) {
throw new AuthException("Cannot register without online mode enabled!");
}else {
try {
String uuid = getMojangUUID(player.getUniqueId());
synchronized(registerUser) {
int cnt;
synchronized(isRegisteredUser) {
isRegisteredUser.setString(1, uuid);
try(ResultSet set = isRegisteredUser.executeQuery()) {
if(set.next()) {
cnt = set.getInt(1);
}else {
throw new SQLException("Empty ResultSet recieved while checking if user exists");
}
}
}
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(HashUtils.EAGLER_SHA256_SALT_SAVE, 0, 32);
byte[] hashed = new byte[32];
digest.doFinal(hashed, 0);
byte[] randomBytes = new byte[32];
synchronized(secureRandom) {
secureRandom.nextBytes(randomBytes);
}
digest.reset();
digest.update(hashed, 0, 32);
digest.update(randomBytes, 0, 32);
digest.update(HashUtils.EAGLER_SHA256_SALT_BASE, 0, 32);
digest.doFinal(hashed, 0);
String username = player.getName();
authLoadingCache.evict(username);
if(cnt > 0) {
synchronized(updatePassword) {
updatePassword.setBytes(1, hashed);
updatePassword.setBytes(2, randomBytes);
updatePassword.setString(3, uuid);
if(updatePassword.executeUpdate() <= 0) {
throw new AuthException("Update password query did not alter the database!");
}
}
}else {
String sockAddr = sockAddrToString(player.getSocketAddress());
if(maxRegistrationsPerIP > 0) {
if(countUsersOnIP(sockAddr) >= maxRegistrationsPerIP) {
throw new TooManyRegisteredOnIPException(sockAddr);
}
}
Date nowDate = new Date(System.currentTimeMillis());
registerUser.setInt(1, 1);
registerUser.setString(2, uuid);
registerUser.setString(3, username);
LoginResult res = ((InitialHandler)player.getPendingConnection()).getLoginProfile();
if(res != null) {
registerUser.setBytes(4, getTexturesProperty(res));
}else {
registerUser.setBytes(4, null);
}
registerUser.setBytes(5, hashed);
registerUser.setBytes(6, randomBytes);
registerUser.setDate(7, nowDate);
registerUser.setString(8, sockAddr);
if(registerUser.executeUpdate() <= 0) {
throw new AuthException("Registration query did not alter the database!");
}
}
}
}catch(SQLException ex) {
throw new AuthException("Failed to query database!", ex);
}
}
}
private static byte[] getTexturesProperty(LoginResult profile) {
try {
Property[] props = profile.getProperties();
for(int i = 0; i < props.length; ++i) {
Property prop = props[i];
if("textures".equals(prop.getName())) {
byte[] texturesData = Base64.decodeBase64(prop.getValue());
byte[] signatureData = prop.getSignature() == null ? ArrayUtils.EMPTY_BYTE_ARRAY : Base64.decodeBase64(prop.getSignature());
ByteArrayOutputStream bao = new ByteArrayOutputStream();
DataOutputStream dao = new DataOutputStream(bao);
dao.writeInt(texturesData.length);
dao.writeInt(signatureData.length);
dao.write(texturesData);
dao.write(signatureData);
return bao.toByteArray();
}
}
}catch(Throwable t) {
}
return null;
}
public int pruneUsers(long before) throws AuthException {
try {
authLoadingCache.flush();
synchronized(pruneUsers) {
pruneUsers.setDate(1, new Date(before));
return pruneUsers.executeUpdate();
}
}catch(SQLException ex) {
throw new AuthException("Failed to query database!", ex);
}
}
public int countUsersOnIP(String addr) throws AuthException {
synchronized(getRegistrationsOnIP) {
try {
getRegistrationsOnIP.setString(1, addr);
try(ResultSet set = getRegistrationsOnIP.executeQuery()) {
if(set.next()) {
return set.getInt(1);
}else {
throw new SQLException("Empty ResultSet recieved while counting accounts");
}
}
}catch(SQLException ex) {
throw new AuthException("Failed to query database!", ex);
}
}
}
public void handleVanillaLogin(PostLoginEvent loginEvent) {
ProxiedPlayer player = loginEvent.getPlayer();
PendingConnection con = player.getPendingConnection();
if(!(con instanceof EaglerInitialHandler)) {
Date lastLogin = null;
String lastLoginIP = null;
boolean isRegistered = false;
synchronized(checkRegistrationByUUID) {
UUID uuid = player.getUniqueId();
try {
String uuidString = getMojangUUID(uuid);
checkRegistrationByUUID.setString(1, getMojangUUID(player.getUniqueId()));
try(ResultSet res = checkRegistrationByUUID.executeQuery()) {
if(res.next()) {
isRegistered = true;
int vers = res.getInt(1);
String username = res.getString(2);
lastLogin = res.getDate(3);
lastLoginIP = res.getString(4);
String playerName = player.getName();
if(!playerName.equals(username)) {
EaglerXBungee.logger().info("Player \"" + uuid.toString() + "\" changed their username from \"" + username
+ "\" to \"" + playerName + "\", updating authentication database...");
synchronized(updateMojangUsername) {
updateMojangUsername.setString(1, playerName);
updateMojangUsername.setString(2, uuidString);
if(updateMojangUsername.executeUpdate() == 0) {
throw new SQLException("Failed to update username to \"" + playerName + "\"");
}
}
}
}
}
byte[] texProperty = getTexturesProperty(((InitialHandler)con).getLoginProfile());
if(texProperty != null) {
synchronized(updateTextures) {
updateTextures.setBytes(1, texProperty);
updateTextures.setString(2, uuidString);
updateTextures.executeUpdate();
}
}
}catch(SQLException ex) {
EaglerXBungee.logger().log(Level.SEVERE, "Could not look up UUID \"" + uuid.toString() + "\" in auth database!", ex);
}
}
if(isRegistered) {
if(lastLogin != null) {
String dateStr;
java.util.Date juLastLogin = new java.util.Date(lastLogin.getTime());
Calendar calendar = Calendar.getInstance();
int yearToday = calendar.get(Calendar.YEAR);
calendar.setTime(juLastLogin);
if(calendar.get(Calendar.YEAR) != yearToday) {
dateStr = (new SimpleDateFormat("EE, MMM d, yyyy, HH:mm z")).format(juLastLogin);
}else {
dateStr = (new SimpleDateFormat("EE, MMM d, HH:mm z")).format(juLastLogin);
}
TextComponent comp = new TextComponent(lastEaglerLoginMessage.replace("$date", dateStr).replace("$ip", "" + lastLoginIP));
comp.setColor(ChatColor.GREEN);
player.sendMessage(comp);
}
player.sendMessage(new TextComponent(useChangeCommandText));
}else {
player.sendMessage(new TextComponent(useRegisterCommandText));
}
}
}
private void destroyStatement(Statement stmt) {
try {
stmt.close();
} catch (SQLException e) {
}
}
public void flush() {
authLoadingCache.flush();
}
public void destroy() {
destroyStatement(registerUser);
destroyStatement(isRegisteredUser);
destroyStatement(pruneUsers);
destroyStatement(updatePassword);
destroyStatement(updateMojangUsername);
destroyStatement(getRegistrationsOnIP);
destroyStatement(checkRegistrationByUUID);
destroyStatement(checkRegistrationByName);
destroyStatement(setLastLogin);
destroyStatement(updateTextures);
try {
databaseConnection.close();
EaglerXBungee.logger().info("Successfully disconnected from database '" + uri + "'");
} catch (SQLException e) {
EaglerXBungee.logger().log(Level.WARNING, "Exception disconnecting from database '" + uri + "'!", e);
}
}
public static class AuthException extends RuntimeException {
public AuthException(String msg) {
super(msg);
}
public AuthException(Throwable t) {
super(t);
}
public AuthException(String msg, Throwable t) {
super(msg, t);
}
}
public static class TooManyRegisteredOnIPException extends AuthException {
public TooManyRegisteredOnIPException(String ip) {
super(ip);
}
}
private static final String hexString = "0123456789abcdef";
private static final char[] HEX = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
public static String getMojangUUID(UUID uuid) {
char[] ret = new char[32];
long msb = uuid.getMostSignificantBits();
long lsb = uuid.getLeastSignificantBits();
for(int i = 0, j; i < 16; ++i) {
j = (15 - i) << 2;
ret[i] = HEX[(int)((msb >> j) & 15l)];
ret[i + 16] = HEX[(int)((lsb >> j) & 15l)];
}
return new String(ret);
}
public static UUID parseMojangUUID(String uuid) {
long msb = 0l;
long lsb = 0l;
for(int i = 0, j; i < 16; ++i) {
j = (15 - i) << 2;
msb |= ((long)hexString.indexOf(uuid.charAt(i)) << j);
lsb |= ((long)hexString.indexOf(uuid.charAt(i + 16)) << j);
}
return new UUID(msb, lsb);
}
private static String sockAddrToString(SocketAddress addr) {
if(addr instanceof InetSocketAddress) {
return ((InetSocketAddress)addr).getAddress().getHostAddress();
}else {
return "127.0.0.1";
}
}
}

View File

@ -1,129 +0,0 @@
/*
* 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.plugin.gateway_bungeecord.auth;
/**
* base implementation of MD4 family style digest as outlined in "Handbook of
* Applied Cryptography", pages 344 - 347.
*/
public abstract class GeneralDigest {
private byte[] xBuf;
private int xBufOff;
private long byteCount;
/**
* Standard constructor
*/
protected GeneralDigest() {
xBuf = new byte[4];
xBufOff = 0;
}
/**
* Copy constructor. We are using copy constructors in place of the
* Object.clone() interface as this interface is not supported by J2ME.
*/
protected GeneralDigest(GeneralDigest t) {
xBuf = new byte[t.xBuf.length];
System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length);
xBufOff = t.xBufOff;
byteCount = t.byteCount;
}
public void update(byte in) {
xBuf[xBufOff++] = in;
if (xBufOff == xBuf.length) {
processWord(xBuf, 0);
xBufOff = 0;
}
byteCount++;
}
public void update(byte[] in, int inOff, int len) {
//
// fill the current word
//
while ((xBufOff != 0) && (len > 0)) {
update(in[inOff]);
inOff++;
len--;
}
//
// process whole words.
//
while (len > xBuf.length) {
processWord(in, inOff);
inOff += xBuf.length;
len -= xBuf.length;
byteCount += xBuf.length;
}
//
// load in the remainder.
//
while (len > 0) {
update(in[inOff]);
inOff++;
len--;
}
}
public void finish() {
long bitLength = (byteCount << 3);
//
// add the pad bytes.
//
update((byte) 128);
while (xBufOff != 0) {
update((byte) 0);
}
processLength(bitLength);
processBlock();
}
public void reset() {
byteCount = 0;
xBufOff = 0;
for (int i = 0; i < xBuf.length; i++) {
xBuf[i] = 0;
}
}
protected abstract void processWord(byte[] in, int inOff);
protected abstract void processLength(long bitLength);
protected abstract void processBlock();
}

View File

@ -1,33 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth;
public class HashUtils {
public static final byte[] EAGLER_SHA256_SALT_BASE = new byte[] { (byte) 117, (byte) 43, (byte) 1, (byte) 112,
(byte) 75, (byte) 3, (byte) 188, (byte) 61, (byte) 121, (byte) 31, (byte) 34, (byte) 181, (byte) 234,
(byte) 31, (byte) 247, (byte) 72, (byte) 12, (byte) 168, (byte) 138, (byte) 45, (byte) 143, (byte) 77,
(byte) 118, (byte) 245, (byte) 187, (byte) 242, (byte) 188, (byte) 219, (byte) 160, (byte) 235, (byte) 235,
(byte) 68 };
public static final byte[] EAGLER_SHA256_SALT_SAVE = new byte[] { (byte) 49, (byte) 25, (byte) 39, (byte) 38,
(byte) 253, (byte) 85, (byte) 70, (byte) 245, (byte) 71, (byte) 150, (byte) 253, (byte) 206, (byte) 4,
(byte) 26, (byte) 198, (byte) 249, (byte) 145, (byte) 251, (byte) 232, (byte) 174, (byte) 186, (byte) 98,
(byte) 27, (byte) 232, (byte) 55, (byte) 144, (byte) 83, (byte) 21, (byte) 36, (byte) 55, (byte) 170,
(byte) 118 };
}

View File

@ -1,247 +0,0 @@
/*
* 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.plugin.gateway_bungeecord.auth;
/**
* implementation of SHA-1 as outlined in "Handbook of Applied Cryptography",
* pages 346 - 349.
*
* It is interesting to ponder why the, apart from the extra IV, the other
* difference here from MD5 is the "endienness" of the word processing!
*/
public class SHA1Digest extends GeneralDigest {
private static final int DIGEST_LENGTH = 20;
private int H1, H2, H3, H4, H5;
private int[] X = new int[80];
private int xOff;
/**
* Standard constructor
*/
public SHA1Digest() {
reset();
}
/**
* Copy constructor. This will copy the state of the provided message digest.
*/
public SHA1Digest(SHA1Digest t) {
super(t);
H1 = t.H1;
H2 = t.H2;
H3 = t.H3;
H4 = t.H4;
H5 = t.H5;
System.arraycopy(t.X, 0, X, 0, t.X.length);
xOff = t.xOff;
}
public String getAlgorithmName() {
return "SHA-1";
}
public int getDigestSize() {
return DIGEST_LENGTH;
}
protected void processWord(byte[] in, int inOff) {
X[xOff++] = ((in[inOff] & 0xff) << 24) | ((in[inOff + 1] & 0xff) << 16) | ((in[inOff + 2] & 0xff) << 8)
| ((in[inOff + 3] & 0xff));
if (xOff == 16) {
processBlock();
}
}
private void unpackWord(int word, byte[] out, int outOff) {
out[outOff] = (byte) (word >>> 24);
out[outOff + 1] = (byte) (word >>> 16);
out[outOff + 2] = (byte) (word >>> 8);
out[outOff + 3] = (byte) word;
}
protected void processLength(long bitLength) {
if (xOff > 14) {
processBlock();
}
X[14] = (int) (bitLength >>> 32);
X[15] = (int) (bitLength & 0xffffffff);
}
public int doFinal(byte[] out, int outOff) {
finish();
unpackWord(H1, out, outOff);
unpackWord(H2, out, outOff + 4);
unpackWord(H3, out, outOff + 8);
unpackWord(H4, out, outOff + 12);
unpackWord(H5, out, outOff + 16);
reset();
return DIGEST_LENGTH;
}
/**
* reset the chaining variables
*/
public void reset() {
super.reset();
H1 = 0x67452301;
H2 = 0xefcdab89;
H3 = 0x98badcfe;
H4 = 0x10325476;
H5 = 0xc3d2e1f0;
xOff = 0;
for (int i = 0; i != X.length; i++) {
X[i] = 0;
}
}
//
// Additive constants
//
private static final int Y1 = 0x5a827999;
private static final int Y2 = 0x6ed9eba1;
private static final int Y3 = 0x8f1bbcdc;
private static final int Y4 = 0xca62c1d6;
private int f(int u, int v, int w) {
return ((u & v) | ((~u) & w));
}
private int h(int u, int v, int w) {
return (u ^ v ^ w);
}
private int g(int u, int v, int w) {
return ((u & v) | (u & w) | (v & w));
}
private int rotateLeft(int x, int n) {
return (x << n) | (x >>> (32 - n));
}
protected void processBlock() {
//
// expand 16 word block into 80 word block.
//
for (int i = 16; i <= 79; i++) {
X[i] = rotateLeft((X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]), 1);
}
//
// set up working variables.
//
int A = H1;
int B = H2;
int C = H3;
int D = H4;
int E = H5;
//
// round 1
//
for (int j = 0; j <= 19; j++) {
int t = rotateLeft(A, 5) + f(B, C, D) + E + X[j] + Y1;
E = D;
D = C;
C = rotateLeft(B, 30);
B = A;
A = t;
}
//
// round 2
//
for (int j = 20; j <= 39; j++) {
int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y2;
E = D;
D = C;
C = rotateLeft(B, 30);
B = A;
A = t;
}
//
// round 3
//
for (int j = 40; j <= 59; j++) {
int t = rotateLeft(A, 5) + g(B, C, D) + E + X[j] + Y3;
E = D;
D = C;
C = rotateLeft(B, 30);
B = A;
A = t;
}
//
// round 4
//
for (int j = 60; j <= 79; j++) {
int t = rotateLeft(A, 5) + h(B, C, D) + E + X[j] + Y4;
E = D;
D = C;
C = rotateLeft(B, 30);
B = A;
A = t;
}
H1 += A;
H2 += B;
H3 += C;
H4 += D;
H5 += E;
//
// reset the offset and clean out the word buffer.
//
xOff = 0;
for (int i = 0; i != X.length; i++) {
X[i] = 0;
}
}
private static final String hex = "0123456789abcdef";
public static String hash2string(byte[] b) {
char[] ret = new char[b.length * 2];
for(int i = 0; i < b.length; ++i) {
int bb = (int)b[i] & 0xFF;
ret[i * 2] = hex.charAt((bb >> 4) & 0xF);
ret[i * 2 + 1] = hex.charAt(bb & 0xF);
}
return new String(ret);
}
}

View File

@ -1,254 +0,0 @@
/*
* 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.plugin.gateway_bungeecord.auth;
public class SHA256Digest extends GeneralDigest {
private static final int DIGEST_LENGTH = 32;
private int H1, H2, H3, H4, H5, H6, H7, H8;
private int[] X = new int[64];
private int xOff;
public SHA256Digest() {
reset();
}
public static int bigEndianToInt(byte[] bs, int off) {
int n = bs[off] << 24;
n |= (bs[++off] & 0xff) << 16;
n |= (bs[++off] & 0xff) << 8;
n |= (bs[++off] & 0xff);
return n;
}
public static void bigEndianToInt(byte[] bs, int off, int[] ns) {
for (int i = 0; i < ns.length; ++i) {
ns[i] = bigEndianToInt(bs, off);
off += 4;
}
}
public static byte[] intToBigEndian(int n) {
byte[] bs = new byte[4];
intToBigEndian(n, bs, 0);
return bs;
}
public static void intToBigEndian(int n, byte[] bs, int off) {
bs[off] = (byte) (n >>> 24);
bs[++off] = (byte) (n >>> 16);
bs[++off] = (byte) (n >>> 8);
bs[++off] = (byte) (n);
}
protected void processWord(byte[] in, int inOff) {
X[xOff] = bigEndianToInt(in, inOff);
if (++xOff == 16) {
processBlock();
}
}
protected void processLength(long bitLength) {
if (xOff > 14) {
processBlock();
}
X[14] = (int) (bitLength >>> 32);
X[15] = (int) (bitLength & 0xffffffff);
}
public int doFinal(byte[] out, int outOff) {
finish();
intToBigEndian(H1, out, outOff);
intToBigEndian(H2, out, outOff + 4);
intToBigEndian(H3, out, outOff + 8);
intToBigEndian(H4, out, outOff + 12);
intToBigEndian(H5, out, outOff + 16);
intToBigEndian(H6, out, outOff + 20);
intToBigEndian(H7, out, outOff + 24);
intToBigEndian(H8, out, outOff + 28);
reset();
return DIGEST_LENGTH;
}
/**
* reset the chaining variables
*/
public void reset() {
super.reset();
/*
* SHA-256 initial hash value The first 32 bits of the fractional parts of the
* square roots of the first eight prime numbers
*/
H1 = 0x6a09e667;
H2 = 0xbb67ae85;
H3 = 0x3c6ef372;
H4 = 0xa54ff53a;
H5 = 0x510e527f;
H6 = 0x9b05688c;
H7 = 0x1f83d9ab;
H8 = 0x5be0cd19;
xOff = 0;
for (int i = 0; i != X.length; i++) {
X[i] = 0;
}
}
protected void processBlock() {
//
// expand 16 word block into 64 word blocks.
//
for (int t = 16; t <= 63; t++) {
X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16];
}
//
// set up working variables.
//
int a = H1;
int b = H2;
int c = H3;
int d = H4;
int e = H5;
int f = H6;
int g = H7;
int h = H8;
int t = 0;
for (int i = 0; i < 8; i++) {
// t = 8 * i
h += Sum1(e) + Ch(e, f, g) + K[t] + X[t];
d += h;
h += Sum0(a) + Maj(a, b, c);
++t;
// t = 8 * i + 1
g += Sum1(d) + Ch(d, e, f) + K[t] + X[t];
c += g;
g += Sum0(h) + Maj(h, a, b);
++t;
// t = 8 * i + 2
f += Sum1(c) + Ch(c, d, e) + K[t] + X[t];
b += f;
f += Sum0(g) + Maj(g, h, a);
++t;
// t = 8 * i + 3
e += Sum1(b) + Ch(b, c, d) + K[t] + X[t];
a += e;
e += Sum0(f) + Maj(f, g, h);
++t;
// t = 8 * i + 4
d += Sum1(a) + Ch(a, b, c) + K[t] + X[t];
h += d;
d += Sum0(e) + Maj(e, f, g);
++t;
// t = 8 * i + 5
c += Sum1(h) + Ch(h, a, b) + K[t] + X[t];
g += c;
c += Sum0(d) + Maj(d, e, f);
++t;
// t = 8 * i + 6
b += Sum1(g) + Ch(g, h, a) + K[t] + X[t];
f += b;
b += Sum0(c) + Maj(c, d, e);
++t;
// t = 8 * i + 7
a += Sum1(f) + Ch(f, g, h) + K[t] + X[t];
e += a;
a += Sum0(b) + Maj(b, c, d);
++t;
}
H1 += a;
H2 += b;
H3 += c;
H4 += d;
H5 += e;
H6 += f;
H7 += g;
H8 += h;
//
// reset the offset and clean out the word buffer.
//
xOff = 0;
for (int i = 0; i < 16; i++) {
X[i] = 0;
}
}
/* SHA-256 functions */
private static int Ch(int x, int y, int z) {
return (x & y) ^ ((~x) & z);
// return z ^ (x & (y ^ z));
}
private static int Maj(int x, int y, int z) {
// return (x & y) ^ (x & z) ^ (y & z);
return (x & y) | (z & (x ^ y));
}
private static int Sum0(int x) {
return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10));
}
private static int Sum1(int x) {
return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7));
}
private static int Theta0(int x) {
return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3);
}
private static int Theta1(int x) {
return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10);
}
/*
* SHA-256 Constants (represent the first 32 bits of the fractional parts of the
* cube roots of the first sixty-four prime numbers)
*/
static final int K[] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4,
0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138,
0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70,
0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa,
0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
}

View File

@ -1,94 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command;
import io.netty.buffer.Unpooled;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
import net.md_5.bungee.BungeeCord;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.OverflowPacketException;
import net.md_5.bungee.protocol.packet.PluginMessage;
public class CommandClientBrand extends Command {
public CommandClientBrand() {
super("client-brand", "eaglercraft.command.clientbrand", "clientbrand");
}
@Override
public void execute(CommandSender var1, String[] var2) {
if(var2.length == 1) {
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(var2[0]);
if(player != null) {
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
EaglerInitialHandler handler = (EaglerInitialHandler)player.getPendingConnection();
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client Brand: " + ChatColor.WHITE + handler.getEaglerBrandString()));
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client Version: " + ChatColor.WHITE + handler.getEaglerVersionString()));
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client UUID: " + ChatColor.WHITE + handler.getClientBrandUUID()));
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Minecraft Client Brand: " + ChatColor.WHITE + decodeMCBrand(handler.getBrandMessage())));
}else {
var1.sendMessage(new TextComponent(ChatColor.RED + "That player is not using eaglercraft!"));
}
}else {
var1.sendMessage(new TextComponent(ChatColor.RED + "That player was not found!"));
}
return;
}
if(var2.length == 2) {
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(var2[1]);
if(player != null) {
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
EaglerInitialHandler handler = (EaglerInitialHandler)player.getPendingConnection();
if("uuid".equalsIgnoreCase(var2[0])) {
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client UUID: " + ChatColor.WHITE + handler.getClientBrandUUID()));
return;
}else if("name".equalsIgnoreCase(var2[0])) {
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client Brand: " + ChatColor.WHITE + handler.getEaglerBrandString()));
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Eagler Client Version: " + ChatColor.WHITE + handler.getEaglerVersionString()));
return;
}else if("mc".equalsIgnoreCase(var2[0])) {
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Minecraft Client Brand: " + ChatColor.WHITE + decodeMCBrand(handler.getBrandMessage())));
return;
}
}else {
var1.sendMessage(new TextComponent(ChatColor.RED + "That player is not using eaglercraft!"));
return;
}
}else {
var1.sendMessage(new TextComponent(ChatColor.RED + "That player was not found!"));
return;
}
}
var1.sendMessage(new TextComponent(ChatColor.RED + "Usage: /client-brand [uuid|name|mc] <username>"));
}
private static String decodeMCBrand(PluginMessage pkt) {
if(pkt == null) {
return "null";
}
try {
return DefinedPacket.readString(Unpooled.wrappedBuffer(pkt.getData()), 64);
}catch(OverflowPacketException | IndexOutOfBoundsException ex) {
return "null";
}
}
}

View File

@ -1,51 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command;
import java.nio.charset.StandardCharsets;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.SHA1Digest;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
public class CommandConfirmCode extends Command {
public static String confirmHash = null;
public CommandConfirmCode() {
super("confirm-code", "eaglercraft.command.confirmcode", "confirmcode");
}
@Override
public void execute(CommandSender var1, String[] var2) {
if(var2.length != 1) {
var1.sendMessage(new TextComponent(ChatColor.RED + "How to use: " + ChatColor.WHITE + "/confirm-code <code>"));
}else {
var1.sendMessage(new TextComponent(ChatColor.YELLOW + "Server list 2FA code has been set to: " + ChatColor.GREEN + var2[0]));
var1.sendMessage(new TextComponent(ChatColor.YELLOW + "You can now return to the server list site and continue"));
byte[] bts = var2[0].getBytes(StandardCharsets.US_ASCII);
SHA1Digest dg = new SHA1Digest();
dg.update(bts, 0, bts.length);
byte[] f = new byte[20];
dg.doFinal(f, 0);
confirmHash = SHA1Digest.hash2string(f);
}
}
}

View File

@ -1,58 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.PendingConnection;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
public class CommandDomain extends Command {
public CommandDomain() {
super("domain", "eaglercraft.command.domain");
}
@Override
public void execute(CommandSender var1, String[] var2) {
if(var2.length != 1) {
var1.sendMessage(new TextComponent(ChatColor.RED + "How to use: " + ChatColor.WHITE + "/domain <player>"));
}else {
ProxiedPlayer player = ProxyServer.getInstance().getPlayer(var2[0]);
if(player == null) {
var1.sendMessage(new TextComponent(ChatColor.RED + "That user is not online"));
return;
}
PendingConnection conn = player.getPendingConnection();
if(!(conn instanceof EaglerInitialHandler)) {
var1.sendMessage(new TextComponent(ChatColor.RED + "That user is not using Eaglercraft"));
return;
}
String origin = ((EaglerInitialHandler)conn).getOrigin();
if(origin != null) {
var1.sendMessage(new TextComponent(ChatColor.BLUE + "Domain of " + var2[0] + " is '" + origin + "'"));
}else {
var1.sendMessage(new TextComponent(ChatColor.RED + "That user's browser did not send an origin header"));
}
}
}
}

View File

@ -1,83 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command;
import java.util.logging.Level;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.DefaultAuthSystem;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.DefaultAuthSystem.AuthException;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerAuthConfig;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.command.ConsoleCommandSender;
public class CommandEaglerPurge extends Command {
public CommandEaglerPurge(String name) {
super(name + "-purge", "eaglercraft.command.purge");
}
@Override
public void execute(CommandSender var1, String[] var2) {
if(var1 instanceof ConsoleCommandSender) {
if(var2.length != 1) {
TextComponent comp = new TextComponent("Use /" + getName() + " <maxAge>");
comp.setColor(ChatColor.RED);
var1.sendMessage(comp);
return;
}
int mx;
try {
mx = Integer.parseInt(var2[0]);
}catch(NumberFormatException ex) {
TextComponent comp = new TextComponent("'" + var2[0] + "' is not an integer!");
comp.setColor(ChatColor.RED);
var1.sendMessage(comp);
return;
}
EaglerAuthConfig authConf = EaglerXBungee.getEagler().getConfig().getAuthConfig();
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
DefaultAuthSystem srv = EaglerXBungee.getEagler().getAuthService();
if(srv != null) {
int cnt;
try {
EaglerXBungee.logger().warning("Console is attempting to purge all accounts with " + mx + " days of inactivity");
cnt = srv.pruneUsers(System.currentTimeMillis() - mx * 86400000l);
}catch(AuthException ex) {
EaglerXBungee.logger().log(Level.SEVERE, "Failed to purge accounts", ex);
TextComponent comp = new TextComponent("Failed to purge, check log! Reason: " + ex.getMessage());
comp.setColor(ChatColor.AQUA);
var1.sendMessage(comp);
return;
}
EaglerXBungee.logger().warning("Console purged " + cnt + " accounts from auth database");
TextComponent comp = new TextComponent("Purged " + cnt + " old accounts from the database");
comp.setColor(ChatColor.AQUA);
var1.sendMessage(comp);
}
}
}else {
TextComponent comp = new TextComponent("This command can only be run from the console!");
comp.setColor(ChatColor.RED);
var1.sendMessage(comp);
}
}
}

View File

@ -1,76 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command;
import java.util.logging.Level;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.DefaultAuthSystem;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerAuthConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.plugin.Command;
public class CommandEaglerRegister extends Command {
public CommandEaglerRegister(String name) {
super(name);
}
@Override
public void execute(CommandSender sender, String[] args) {
if(sender instanceof ProxiedPlayer) {
ProxiedPlayer player = (ProxiedPlayer)sender;
if(args.length != 1) {
TextComponent comp = new TextComponent("Use: /" + getName() + " <password>");
comp.setColor(ChatColor.RED);
player.sendMessage(comp);
return;
}
EaglerAuthConfig authConf = EaglerXBungee.getEagler().getConfig().getAuthConfig();
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
DefaultAuthSystem srv = EaglerXBungee.getEagler().getAuthService();
if(srv != null) {
if(!(player.getPendingConnection() instanceof EaglerInitialHandler)) {
try {
srv.processSetPassword(player, args[0]);
sender.sendMessage(new TextComponent(authConf.getCommandSuccessText()));
}catch(DefaultAuthSystem.TooManyRegisteredOnIPException ex) {
String tooManyReg = authConf.getTooManyRegistrationsMessage();
sender.sendMessage(new TextComponent(tooManyReg));
}catch(DefaultAuthSystem.AuthException ex) {
EaglerXBungee.logger().log(Level.SEVERE, "Internal exception while processing password change from \"" + player.getName() + "\"", ex);
TextComponent comp = new TextComponent("Internal error, check console logs");
comp.setColor(ChatColor.RED);
sender.sendMessage(comp);
}
}else {
player.sendMessage(new TextComponent(authConf.getNeedVanillaToRegisterMessage()));
}
}
}
}else {
TextComponent comp = new TextComponent("You must be a player to use this command!");
comp.setColor(ChatColor.RED);
sender.sendMessage(comp);
}
}
}

View File

@ -1,89 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.command;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerBungeeConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerRateLimiter;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.CommandSender;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.plugin.Command;
public class CommandRatelimit extends Command {
public CommandRatelimit() {
super("ratelimit", "eaglercraft.command.ratelimit");
}
@Override
public void execute(CommandSender sender, String[] args) {
if((args.length != 1 && args.length != 2) || !args[0].equalsIgnoreCase("reset")) {
TextComponent comp = new TextComponent("Usage: /ratelimit reset [ip|login|motd|query]"); //TODO: allow reset ratelimit on specific listeners
comp.setColor(ChatColor.RED);
sender.sendMessage(comp);
}else {
int resetNum = 0;
if(args.length == 2) {
if(args[1].equalsIgnoreCase("ip")) {
resetNum = 1;
}else if(args[1].equalsIgnoreCase("login")) {
resetNum = 2;
}else if(args[1].equalsIgnoreCase("motd")) {
resetNum = 3;
}else if(args[1].equalsIgnoreCase("query")) {
resetNum = 4;
}else {
TextComponent comp = new TextComponent("Unknown ratelimit '" + args[1] + "'!");
comp.setColor(ChatColor.RED);
sender.sendMessage(comp);
return;
}
}
EaglerBungeeConfig conf = EaglerXBungee.getEagler().getConfig();
for(EaglerListenerConfig listener : conf.getServerListeners()) {
if(resetNum == 0 || resetNum == 1) {
EaglerRateLimiter limiter = listener.getRatelimitIp();
if(limiter != null) {
limiter.reset();
}
}
if(resetNum == 0 || resetNum == 2) {
EaglerRateLimiter limiter = listener.getRatelimitLogin();
if(limiter != null) {
limiter.reset();
}
}
if(resetNum == 0 || resetNum == 3) {
EaglerRateLimiter limiter = listener.getRatelimitMOTD();
if(limiter != null) {
limiter.reset();
}
}
if(resetNum == 0 || resetNum == 4) {
EaglerRateLimiter limiter = listener.getRatelimitQuery();
if(limiter != null) {
limiter.reset();
}
}
}
sender.sendMessage(new TextComponent("Ratelimits reset."));
}
}
}

View File

@ -1,166 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.config.Configuration;
public class EaglerAuthConfig {
static EaglerAuthConfig loadConfig(Configuration config) {
boolean enableAuthentication = config.getBoolean("enable_authentication_system");
boolean useBuiltInAuthentication = config.getBoolean("use_onboard_eaglerx_system");
String databaseURI = config.getString("auth_db_uri");
String driverClass = config.getString("sql_driver_class", "internal");
String driverPath = config.getString("sql_driver_path", null);
String passwordPromptScreenText = ChatColor.translateAlternateColorCodes('&', config.getString("password_prompt_screen_text", ""));
String notRegisteredScreenText = ChatColor.translateAlternateColorCodes('&', config.getString("not_registered_screen_text", ""));
String wrongPasswordScreenText = ChatColor.translateAlternateColorCodes('&', config.getString("wrong_password_screen_text", ""));
String eaglerCommandName = config.getString("eagler_command_name");
String useRegisterCommandText = ChatColor.translateAlternateColorCodes('&', config.getString("use_register_command_text", ""));
String useChangeCommandText = ChatColor.translateAlternateColorCodes('&', config.getString("use_change_command_text", ""));
String commandSuccessText = ChatColor.translateAlternateColorCodes('&', config.getString("command_success_text", ""));
String lastEaglerLoginMessage = ChatColor.translateAlternateColorCodes('&', config.getString("last_eagler_login_message", ""));
String tooManyRegistrationsMessage = ChatColor.translateAlternateColorCodes('&', config.getString("too_many_registrations_message", ""));
String needVanillaToRegisterMessage = ChatColor.translateAlternateColorCodes('&', config.getString("need_vanilla_to_register_message", ""));
boolean overrideEaglerToVanillaSkins = config.getBoolean("override_eagler_to_vanilla_skins");
int maxRegistrationsPerIP = config.getInt("max_registration_per_ip", -1);
return new EaglerAuthConfig(enableAuthentication, useBuiltInAuthentication, databaseURI, driverClass,
driverPath, passwordPromptScreenText, wrongPasswordScreenText, notRegisteredScreenText,
eaglerCommandName, useRegisterCommandText, useChangeCommandText, commandSuccessText,
lastEaglerLoginMessage, tooManyRegistrationsMessage, needVanillaToRegisterMessage,
overrideEaglerToVanillaSkins, maxRegistrationsPerIP);
}
private boolean enableAuthentication;
private boolean useBuiltInAuthentication;
private final String databaseURI;
private final String driverClass;
private final String driverPath;
private final String passwordPromptScreenText;
private final String wrongPasswordScreenText;
private final String notRegisteredScreenText;
private final String eaglerCommandName;
private final String useRegisterCommandText;
private final String useChangeCommandText;
private final String commandSuccessText;
private final String lastEaglerLoginMessage;
private final String tooManyRegistrationsMessage;
private final String needVanillaToRegisterMessage;
private final boolean overrideEaglerToVanillaSkins;
private final int maxRegistrationsPerIP;
private EaglerAuthConfig(boolean enableAuthentication, boolean useBuiltInAuthentication, String databaseURI,
String driverClass, String driverPath, String passwordPromptScreenText, String wrongPasswordScreenText,
String notRegisteredScreenText, String eaglerCommandName, String useRegisterCommandText,
String useChangeCommandText, String commandSuccessText, String lastEaglerLoginMessage,
String tooManyRegistrationsMessage, String needVanillaToRegisterMessage,
boolean overrideEaglerToVanillaSkins, int maxRegistrationsPerIP) {
this.enableAuthentication = enableAuthentication;
this.useBuiltInAuthentication = useBuiltInAuthentication;
this.databaseURI = databaseURI;
this.driverClass = driverClass;
this.driverPath = driverPath;
this.passwordPromptScreenText = passwordPromptScreenText;
this.wrongPasswordScreenText = wrongPasswordScreenText;
this.notRegisteredScreenText = notRegisteredScreenText;
this.eaglerCommandName = eaglerCommandName;
this.useRegisterCommandText = useRegisterCommandText;
this.useChangeCommandText = useChangeCommandText;
this.commandSuccessText = commandSuccessText;
this.lastEaglerLoginMessage = lastEaglerLoginMessage;
this.tooManyRegistrationsMessage = tooManyRegistrationsMessage;
this.needVanillaToRegisterMessage = needVanillaToRegisterMessage;
this.overrideEaglerToVanillaSkins = overrideEaglerToVanillaSkins;
this.maxRegistrationsPerIP = maxRegistrationsPerIP;
}
public boolean isEnableAuthentication() {
return enableAuthentication;
}
public boolean isUseBuiltInAuthentication() {
return useBuiltInAuthentication;
}
public void triggerOnlineModeDisabled() {
enableAuthentication = false;
useBuiltInAuthentication = false;
}
public String getDatabaseURI() {
return databaseURI;
}
public String getDriverClass() {
return driverClass;
}
public String getDriverPath() {
return driverPath;
}
public String getPasswordPromptScreenText() {
return passwordPromptScreenText;
}
public String getWrongPasswordScreenText() {
return wrongPasswordScreenText;
}
public String getNotRegisteredScreenText() {
return notRegisteredScreenText;
}
public String getEaglerCommandName() {
return eaglerCommandName;
}
public String getUseRegisterCommandText() {
return useRegisterCommandText;
}
public String getUseChangeCommandText() {
return useChangeCommandText;
}
public String getCommandSuccessText() {
return commandSuccessText;
}
public String getLastEaglerLoginMessage() {
return lastEaglerLoginMessage;
}
public String getTooManyRegistrationsMessage() {
return tooManyRegistrationsMessage;
}
public String getNeedVanillaToRegisterMessage() {
return needVanillaToRegisterMessage;
}
public boolean getOverrideEaglerToVanillaSkins() {
return overrideEaglerToVanillaSkins;
}
public int getMaxRegistrationsPerIP() {
return maxRegistrationsPerIP;
}
}

View File

@ -1,566 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
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.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpContentType;
import net.md_5.bungee.config.Configuration;
import net.md_5.bungee.config.ConfigurationProvider;
import net.md_5.bungee.config.YamlConfiguration;
import net.md_5.bungee.protocol.Property;
public class EaglerBungeeConfig {
public static EaglerBungeeConfig loadConfig(File directory) throws IOException {
Map<String, HttpContentType> contentTypes = new HashMap<>();
try(InputStream is = new FileInputStream(getConfigFile(directory, "http_mime_types.json"))) {
loadMimeTypes(is, contentTypes);
}catch(Throwable t) {
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_http_mime_types.json")) {
loadMimeTypes(is, contentTypes);
}catch(IOException ex) {
EaglerXBungee.logger().severe("Could not load default_http_mime_types.json!");
throw new RuntimeException(ex);
}
}
directory.mkdirs();
ConfigurationProvider prov = ConfigurationProvider.getProvider(YamlConfiguration.class);
Configuration configYml = prov.load(getConfigFile(directory, "settings.yml"));
String serverName = configYml.getString("server_name", "EaglercraftXBungee Server");
EaglerXBungeeAPIHelper.getTemplateGlobals().put("server_name", serverName);
String serverUUIDString = configYml.getString("server_uuid", null);
if(serverUUIDString == null) {
throw new IOException("You must specify a server_uuid!");
}
UUID serverUUID = null;
try {
serverUUID = UUID.fromString(serverUUIDString);
}catch(Throwable t) {
}
if(serverUUID == null) {
throw new IOException("The server_uuid \"" + serverUUIDString + "\" is invalid!");
}
Configuration listenerYml = prov.load(getConfigFile(directory, "listeners.yml"));
Iterator<String> listeners = listenerYml.getKeys().iterator();
Map<String, EaglerListenerConfig> serverListeners = new HashMap<>();
boolean voiceChat = false;
while(listeners.hasNext()) {
String name = listeners.next();
EaglerListenerConfig conf = EaglerListenerConfig.loadConfig(listenerYml.getSection(name), contentTypes);
if(conf != null) {
serverListeners.put(name, conf);
voiceChat |= conf.getEnableVoiceChat();
}else {
EaglerXBungee.logger().severe("Invalid listener config: " + name);
}
}
if(serverListeners.size() == 0) {
EaglerXBungee.logger().severe("No Listeners Configured!");
}
Configuration authserivceYml = prov.load(getConfigFile(directory, "authservice.yml"));
EaglerAuthConfig authConfig = EaglerAuthConfig.loadConfig(authserivceYml);
Configuration updatesYml = prov.load(getConfigFile(directory, "updates.yml"));
EaglerUpdateConfig updatesConfig = EaglerUpdateConfig.loadConfig(updatesYml);
Configuration iceServersYml = prov.load(getConfigFile(directory, "ice_servers.yml"));
Collection<String> iceServers = loadICEServers(iceServersYml);
if(authConfig.isEnableAuthentication()) {
for(EaglerListenerConfig lst : serverListeners.values()) {
if(lst.getRatelimitLogin() != null) lst.getRatelimitLogin().setDivisor(2);
if(lst.getRatelimitIp() != null) lst.getRatelimitIp().setDivisor(2);
}
}
File pauseMenuFolder = new File(directory, "pause_menu");
if(!pauseMenuFolder.isDirectory() && !pauseMenuFolder.mkdir()) {
throw new IOException("Could not create directory: " + pauseMenuFolder.getAbsolutePath());
}
File pauseMenuYml = new File(pauseMenuFolder, "pause_menu.yml");
if(!pauseMenuYml.isFile()) {
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_pause_menu.yml")) {
copyConfigFile(is, pauseMenuYml);
}
File f2 = new File(pauseMenuFolder, "server_info.html");
if(!f2.isFile()) {
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_pause_menu_server_info.html")) {
copyConfigFile(is, f2);
}
}
f2 = new File(pauseMenuFolder, "test_image.png");
if(!f2.isFile()) {
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_pause_menu_test_image.png")) {
copyBinaryFile(is, f2);
}
}
f2 = new File(pauseMenuFolder, "message_api_example.html");
if(!f2.isFile()) {
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_message_api_example.html")) {
copyConfigFile(is, f2);
}
}
f2 = new File(pauseMenuFolder, "message_api_v1.js");
if(!f2.isFile()) {
try(InputStream is = EaglerBungeeConfig.class.getResourceAsStream("default_message_api_v1.js")) {
copyConfigFile(is, f2);
}
}
}
EaglerPauseMenuConfig pauseMenuConfig = EaglerPauseMenuConfig.loadConfig(prov.load(pauseMenuYml), pauseMenuFolder);
long websocketKeepAliveTimeout = configYml.getInt("websocket_connection_timeout", 15000);
long websocketHandshakeTimeout = configYml.getInt("websocket_handshake_timeout", 5000);
long builtinHttpServerTimeout = configYml.getInt("builtin_http_server_timeout", 10000);
int websocketCompressionLevel = configYml.getInt("http_websocket_compression_level", 6);
boolean downloadVanillaSkins = configYml.getBoolean("download_vanilla_skins_to_clients", false);
Collection<String> validSkinUrls = (Collection<String>)configYml.getList("valid_skin_download_urls");
int uuidRateLimitPlayer = configYml.getInt("uuid_lookup_ratelimit_player", 50);
int uuidRateLimitGlobal = configYml.getInt("uuid_lookup_ratelimit_global", 175);
int skinRateLimitPlayer = configYml.getInt("skin_download_ratelimit_player", 1000);
int skinRateLimitGlobal = configYml.getInt("skin_download_ratelimit_global", 30000);
String skinCacheURI = configYml.getString("skin_cache_db_uri", "jdbc:sqlite:eaglercraft_skins_cache.db");
int keepObjectsDays = configYml.getInt("skin_cache_keep_objects_days", 45);
int keepProfilesDays = configYml.getInt("skin_cache_keep_profiles_days", 7);
int maxObjects = configYml.getInt("skin_cache_max_objects", 32768);
int maxProfiles = configYml.getInt("skin_cache_max_profiles", 32768);
int antagonistsRateLimit = configYml.getInt("skin_cache_antagonists_ratelimit", 15);
String sqliteDriverClass = configYml.getString("sql_driver_class", "internal");
String sqliteDriverPath = configYml.getString("sql_driver_path", null);
String eaglerPlayersVanillaSkin = configYml.getString("eagler_players_vanilla_skin", null);
if(eaglerPlayersVanillaSkin != null && eaglerPlayersVanillaSkin.length() == 0) {
eaglerPlayersVanillaSkin = null;
}
boolean enableIsEaglerPlayerProperty = configYml.getBoolean("enable_is_eagler_player_property", true);
Set<String> disableVoiceOnServers = new HashSet<>((Collection<String>)configYml.getList("disable_voice_chat_on_servers"));
boolean disableFNAWSkinsEverywhere = configYml.getBoolean("disable_fnaw_skins_everywhere", false);
Set<String> disableFNAWSkinsOnServers = new HashSet<>((Collection<String>)configYml.getList("disable_fnaw_skins_on_servers"));
boolean enableBackendRPCAPI = configYml.getBoolean("enable_backend_rpc_api", false);
boolean useModernizedChannelNames = configYml.getBoolean("use_modernized_channel_names", false);
final EaglerBungeeConfig ret = new EaglerBungeeConfig(serverName, serverUUID, websocketKeepAliveTimeout,
websocketHandshakeTimeout, builtinHttpServerTimeout, websocketCompressionLevel, serverListeners,
contentTypes, downloadVanillaSkins, validSkinUrls, uuidRateLimitPlayer, uuidRateLimitGlobal,
skinRateLimitPlayer, skinRateLimitGlobal, skinCacheURI, keepObjectsDays, keepProfilesDays, maxObjects,
maxProfiles, antagonistsRateLimit, sqliteDriverClass, sqliteDriverPath, eaglerPlayersVanillaSkin,
enableIsEaglerPlayerProperty, authConfig, updatesConfig, iceServers, voiceChat, disableVoiceOnServers,
disableFNAWSkinsEverywhere, disableFNAWSkinsOnServers, enableBackendRPCAPI, useModernizedChannelNames,
pauseMenuConfig);
if(eaglerPlayersVanillaSkin != null) {
VanillaDefaultSkinProfileLoader.lookupVanillaSkinUser(ret);
}
return ret;
}
private static File getConfigFile(File directory, String fileName) throws IOException {
File file = new File(directory, fileName);
if(!file.isFile()) {
try (BufferedReader is = new BufferedReader(new InputStreamReader(
EaglerBungeeConfig.class.getResourceAsStream("default_" + fileName), StandardCharsets.UTF_8));
PrintWriter os = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
String line;
while((line = is.readLine()) != null) {
if(line.contains("${")) {
line = line.replace("${random_uuid}", UUID.randomUUID().toString());
}
os.println(line);
}
}
}
return file;
}
private static void copyConfigFile(InputStream is, File file) throws IOException {
try(PrintWriter os = new PrintWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8));
String line;
while((line = reader.readLine()) != null) {
os.println(line);
}
}
}
private static void copyBinaryFile(InputStream is, File file) throws IOException {
try(OutputStream os = new FileOutputStream(file)) {
byte[] copyBuffer = new byte[1024];
int i;
while((i = is.read(copyBuffer)) != -1) {
os.write(copyBuffer, 0, i);
}
}
}
private static void loadMimeTypes(InputStream file, Map<String, HttpContentType> contentTypes) throws IOException {
JsonObject obj = parseJsonObject(file);
for(Entry<String, JsonElement> etr : obj.entrySet()) {
String mime = etr.getKey();
try {
JsonObject e = etr.getValue().getAsJsonObject();
JsonArray arr = e.getAsJsonArray("files");
if(arr == null || arr.size() == 0) {
EaglerXBungee.logger().warning("MIME type '" + mime + "' defines no extensions!");
continue;
}
HashSet<String> exts = new HashSet<>();
for(int i = 0, l = arr.size(); i < l; ++i) {
exts.add(arr.get(i).getAsString());
}
long expires = 0l;
JsonElement ex = e.get("expires");
if(ex != null) {
expires = ex.getAsInt() * 1000l;
}
String charset = null;
ex = e.get("charset");
if(ex != null) {
charset = ex.getAsString();
}
HttpContentType typeObj = new HttpContentType(exts, mime, charset, expires);
for(String s : exts) {
contentTypes.put(s, typeObj);
}
}catch(Throwable t) {
EaglerXBungee.logger().warning("Exception parsing MIME type '" + mime + "' - " + t.toString());
}
}
}
private static Collection<String> loadICEServers(Configuration config) {
Collection<String> ret = new ArrayList<>(config.contains("voice_stun_servers") ? (List<String>)config.getList("voice_stun_servers") : (List<String>)config.getList("voice_servers_no_passwd"));
Configuration turnServers = config.contains("voice_turn_servers") ? config.getSection("voice_turn_servers") : config.getSection("voice_servers_passwd");
Iterator<String> turnItr = turnServers.getKeys().iterator();
while(turnItr.hasNext()) {
String name = turnItr.next();
Configuration turnSvr = turnServers.getSection(name);
ret.add(turnSvr.getString("url") + ";" + turnSvr.getString("username") + ";" + turnSvr.getString("password"));
}
return ret;
}
private static JsonObject parseJsonObject(InputStream file) throws IOException {
try {
return JsonParser.parseReader(new InputStreamReader(file, StandardCharsets.UTF_8)).getAsJsonObject();
}catch(JsonSyntaxException ex) {
throw new IOException("Invalid JSONObject", ex);
}
}
public static final Property isEaglerProperty = new Property("isEaglerPlayer", "true");
private final String serverName;
private final UUID serverUUID;
private final long websocketKeepAliveTimeout;
private final long websocketHandshakeTimeout;
private final long builtinHttpServerTimeout;
private final int httpWebsocketCompressionLevel;
private final Map<String, EaglerListenerConfig> serverListeners;
private final Map<String, HttpContentType> contentTypes;
private final boolean downloadVanillaSkins;
private final Collection<String> validSkinUrls;
private final int uuidRateLimitPlayer;
private final int uuidRateLimitGlobal;
private final int skinRateLimitPlayer;
private final int skinRateLimitGlobal;
private final String skinCacheURI;
private final int keepObjectsDays;
private final int keepProfilesDays;
private final int maxObjects;
private final int maxProfiles;
private final int antagonistsRateLimit;
private final String sqliteDriverClass;
private final String sqliteDriverPath;
private final String eaglerPlayersVanillaSkin;
private final boolean enableIsEaglerPlayerProperty;
private final EaglerAuthConfig authConfig;
private final EaglerUpdateConfig updateConfig;
private final Collection<String> iceServers;
private final boolean enableVoiceChat;
private final Set<String> disableVoiceOnServers;
private final boolean disableFNAWSkinsEverywhere;
private final Set<String> disableFNAWSkinsOnServers;
private final boolean enableBackendRPCAPI;
private final boolean useModernizedChannelNames;
private final EaglerPauseMenuConfig pauseMenuConf;
private boolean isCrackedFlag;
Property[] eaglerPlayersVanillaSkinCached = new Property[] { isEaglerProperty };
public String getServerName() {
return serverName;
}
public UUID getServerUUID() {
return serverUUID;
}
public long getWebsocketKeepAliveTimeout() {
return websocketKeepAliveTimeout;
}
public long getWebsocketHandshakeTimeout() {
return websocketHandshakeTimeout;
}
public long getBuiltinHttpServerTimeout() {
return builtinHttpServerTimeout;
}
public int getHttpWebsocketCompressionLevel() {
return httpWebsocketCompressionLevel;
}
public Collection<EaglerListenerConfig> getServerListeners() {
return serverListeners.values();
}
public EaglerListenerConfig getServerListenersByName(String name) {
return serverListeners.get(name);
}
public Map<String, HttpContentType> getContentType() {
return contentTypes;
}
public Map<String, HttpContentType> getContentTypes() {
return contentTypes;
}
public boolean getDownloadVanillaSkins() {
return downloadVanillaSkins;
}
public Collection<String> getValidSkinUrls() {
return validSkinUrls;
}
public boolean isValidSkinHost(String host) {
host = host.toLowerCase();
for(String str : validSkinUrls) {
if(str.length() > 0) {
str = str.toLowerCase();
if(str.charAt(0) == '*') {
if(host.endsWith(str.substring(1))) {
return true;
}
}else {
if(host.equals(str)) {
return true;
}
}
}
}
return false;
}
public int getUuidRateLimitPlayer() {
return uuidRateLimitPlayer;
}
public int getUuidRateLimitGlobal() {
return uuidRateLimitGlobal;
}
public int getSkinRateLimitPlayer() {
return skinRateLimitPlayer;
}
public int getSkinRateLimitGlobal() {
return skinRateLimitGlobal;
}
public String getSkinCacheURI() {
return skinCacheURI;
}
public String getSQLiteDriverClass() {
return sqliteDriverClass;
}
public String getSQLiteDriverPath() {
return sqliteDriverPath;
}
public int getKeepObjectsDays() {
return keepObjectsDays;
}
public int getKeepProfilesDays() {
return keepProfilesDays;
}
public int getMaxObjects() {
return maxObjects;
}
public int getMaxProfiles() {
return maxProfiles;
}
public int getAntagonistsRateLimit() {
return antagonistsRateLimit;
}
public String getEaglerPlayersVanillaSkin() {
return eaglerPlayersVanillaSkin;
}
public boolean getEnableIsEaglerPlayerProperty() {
return enableIsEaglerPlayerProperty;
}
public Property[] getEaglerPlayersVanillaSkinProperties() {
return eaglerPlayersVanillaSkinCached;
}
public boolean isCracked() {
return isCrackedFlag;
}
public void setCracked(boolean cracked) {
isCrackedFlag = cracked;
}
public EaglerAuthConfig getAuthConfig() {
return authConfig;
}
public EaglerUpdateConfig getUpdateConfig() {
return updateConfig;
}
public Collection<String> getICEServers() {
return iceServers;
}
public boolean getEnableVoiceChat() {
return enableVoiceChat;
}
public Set<String> getDisableVoiceOnServersSet() {
return disableVoiceOnServers;
}
public boolean getDisableFNAWSkinsEverywhere() {
return disableFNAWSkinsEverywhere;
}
public Set<String> getDisableFNAWSkinsOnServersSet() {
return disableFNAWSkinsOnServers;
}
public boolean getEnableBackendRPCAPI() {
return enableBackendRPCAPI;
}
public boolean getUseModernizedChannelNames() {
return useModernizedChannelNames;
}
public EaglerPauseMenuConfig getPauseMenuConf() {
return pauseMenuConf;
}
private EaglerBungeeConfig(String serverName, UUID serverUUID, long websocketKeepAliveTimeout,
long websocketHandshakeTimeout, long builtinHttpServerTimeout, int httpWebsocketCompressionLevel,
Map<String, EaglerListenerConfig> serverListeners, Map<String, HttpContentType> contentTypes,
boolean downloadVanillaSkins, Collection<String> validSkinUrls, int uuidRateLimitPlayer,
int uuidRateLimitGlobal, int skinRateLimitPlayer, int skinRateLimitGlobal, String skinCacheURI,
int keepObjectsDays, int keepProfilesDays, int maxObjects, int maxProfiles, int antagonistsRateLimit,
String sqliteDriverClass, String sqliteDriverPath, String eaglerPlayersVanillaSkin,
boolean enableIsEaglerPlayerProperty, EaglerAuthConfig authConfig, EaglerUpdateConfig updateConfig,
Collection<String> iceServers, boolean enableVoiceChat, Set<String> disableVoiceOnServers,
boolean disableFNAWSkinsEverywhere, Set<String> disableFNAWSkinsOnServers,
boolean enableBackendRPCAPI, boolean useModernizedChannelNames, EaglerPauseMenuConfig pauseMenuConf) {
this.serverName = serverName;
this.serverUUID = serverUUID;
this.serverListeners = serverListeners;
this.websocketHandshakeTimeout = websocketHandshakeTimeout;
this.websocketKeepAliveTimeout = websocketKeepAliveTimeout;
this.builtinHttpServerTimeout = builtinHttpServerTimeout;
this.httpWebsocketCompressionLevel = httpWebsocketCompressionLevel;
this.contentTypes = contentTypes;
this.downloadVanillaSkins = downloadVanillaSkins;
this.validSkinUrls = validSkinUrls;
this.uuidRateLimitPlayer = uuidRateLimitPlayer;
this.uuidRateLimitGlobal = uuidRateLimitGlobal;
this.skinRateLimitPlayer = skinRateLimitPlayer;
this.skinRateLimitGlobal = skinRateLimitGlobal;
this.skinCacheURI = skinCacheURI;
this.keepObjectsDays = keepObjectsDays;
this.keepProfilesDays = keepProfilesDays;
this.maxObjects = maxObjects;
this.maxProfiles = maxProfiles;
this.antagonistsRateLimit = antagonistsRateLimit;
this.sqliteDriverClass = sqliteDriverClass;
this.sqliteDriverPath = sqliteDriverPath;
this.eaglerPlayersVanillaSkin = eaglerPlayersVanillaSkin;
this.enableIsEaglerPlayerProperty = enableIsEaglerPlayerProperty;
this.authConfig = authConfig;
this.updateConfig = updateConfig;
this.iceServers = iceServers;
this.enableVoiceChat = enableVoiceChat;
this.disableVoiceOnServers = disableVoiceOnServers;
this.disableFNAWSkinsEverywhere = disableFNAWSkinsEverywhere;
this.disableFNAWSkinsOnServers = disableFNAWSkinsOnServers;
this.enableBackendRPCAPI = enableBackendRPCAPI;
this.useModernizedChannelNames = useModernizedChannelNames;
this.pauseMenuConf = pauseMenuConf;
}
}

View File

@ -1,360 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import io.netty.handler.codec.http.HttpRequest;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpContentType;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpWebServer;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.config.ListenerInfo;
import net.md_5.bungee.config.Configuration;
public class EaglerListenerConfig extends ListenerInfo {
static EaglerListenerConfig loadConfig(Configuration config, Map<String, HttpContentType> contentTypes) {
String host = config.getString("address", "0.0.0.0:8081");
InetSocketAddress hostv4 = null;
if(host != null && !host.equalsIgnoreCase("null") && !host.equalsIgnoreCase("none")) {
int i = host.lastIndexOf(':');
if(i == -1) {
throw new IllegalArgumentException("Invalid address: " + host + "! Must be an ipv4:port combo");
}
hostv4 = new InetSocketAddress(host.substring(0, i), Integer.parseInt(host.substring(i + 1)));
}
String hostV6 = config.getString("address_v6", "null");
InetSocketAddress hostv6 = null;
if(hostV6 != null && !hostV6.equalsIgnoreCase("null") && !hostV6.equalsIgnoreCase("none") && hostV6.length() > 0) {
int i = hostV6.lastIndexOf(':');
if(i == -1) {
throw new IllegalArgumentException("Invalid address: " + host + "! Must be an ipv6:port combo");
}
hostv6 = new InetSocketAddress(hostV6.substring(0, i), Integer.parseInt(hostV6.substring(i + 1)));
}
if(hostv4 == null && hostv6 == null) {
throw new IllegalArgumentException("Invalid host specifies no addresses, both v4 and v6 address are null");
}
int maxPlayer = config.getInt("max_players", 60);
String tabListType = config.getString("tab_list", "GLOBAL_PING");
String defaultServer = config.getString("default_server", "lobby");
boolean forceDefaultServer = config.getBoolean("force_default_server", false);
boolean forwardIp = config.getBoolean("forward_ip", false);
String forwardIpHeader = config.getString("forward_ip_header", "X-Real-IP");
String redirectLegacyClientsTo = config.getString("redirect_legacy_clients_to", "null");
if(redirectLegacyClientsTo != null && (redirectLegacyClientsTo.equalsIgnoreCase("null") || redirectLegacyClientsTo.length() == 0)) {
redirectLegacyClientsTo = null;
}
String serverIcon = config.getString("server_icon", "server-icon.png");
List<String> serverMOTD = (List<String>) config.getList("server_motd", Arrays.asList("&6An EaglercraftX server"));
for(int i = 0, l = serverMOTD.size(); i < l; ++i) {
serverMOTD.set(i, ChatColor.translateAlternateColorCodes('&', serverMOTD.get(i)));
}
boolean allowMOTD = config.getBoolean("allow_motd", true);
boolean allowQuery = config.getBoolean("allow_query", true);
int minMCProtocol = config.getInt("min_minecraft_protocol", 47);
int maxMCProtocol = config.getInt("max_minecraft_protocol", 340);
boolean allowV3 = config.getBoolean("allow_protocol_v3", true);
boolean allowV4 = config.getBoolean("allow_protocol_v4", true);
if(!allowV3 && !allowV4) {
throw new IllegalArgumentException("Both v3 and v4 protocol are disabled!");
}
int defragSendDelay = config.getInt("protocol_v4_defrag_send_delay", 10);
boolean haproxyProtocol = config.getBoolean("use_haproxy_protocol", false);
int cacheTTL = 7200;
boolean cacheAnimation = false;
boolean cacheResults = true;
boolean cacheTrending = true;
boolean cachePortfolios = false;
Configuration cacheConf = config.getSection("request_motd_cache");
if(cacheConf != null) {
cacheTTL = cacheConf.getInt("cache_ttl", 7200);
cacheAnimation = cacheConf.getBoolean("online_server_list_animation", false);
cacheResults = cacheConf.getBoolean("online_server_list_results", true);
cacheTrending = cacheConf.getBoolean("online_server_list_trending", true);
cachePortfolios = cacheConf.getBoolean("online_server_list_portfolios", false);
}
HttpWebServer httpServer = null;
Configuration httpServerConf = config.getSection("http_server");
if(httpServerConf != null && httpServerConf.getBoolean("enabled", false)) {
String rootDirectory = httpServerConf.getString("root", "web");
String page404 = httpServerConf.getString("page_404_not_found", "default");
if(page404 != null && (page404.length() == 0 || page404.equalsIgnoreCase("null") || page404.equalsIgnoreCase("default"))) {
page404 = null;
}
List<String> defaultIndex = Arrays.asList("index.html", "index.htm");
List<?> indexPageRaw = httpServerConf.getList("page_index_name", defaultIndex);
List<String> indexPage = new ArrayList<>(indexPageRaw.size());
for(int i = 0, l = indexPageRaw.size(); i < l; ++i) {
Object o = indexPageRaw.get(i);
if(o instanceof String) {
indexPage.add((String)o);
}
}
if(indexPage.size() == 0) {
indexPage.addAll(defaultIndex);
}
httpServer = new HttpWebServer(new File(EaglerXBungee.getEagler().getDataFolder(), rootDirectory),
contentTypes, indexPage, page404);
}
boolean enableVoiceChat = config.getBoolean("allow_voice", false);
EaglerRateLimiter ratelimitIp = null;
EaglerRateLimiter ratelimitLogin = null;
EaglerRateLimiter ratelimitMOTD = null;
EaglerRateLimiter ratelimitQuery = null;
Configuration rateLimitConfig = config.getSection("ratelimit");
if(rateLimitConfig != null) {
Configuration ratelimitIpConfig = rateLimitConfig.getSection("ip");
if(ratelimitIpConfig != null && ratelimitIpConfig.getBoolean("enable", false)) {
ratelimitIp = EaglerRateLimiter.loadConfig(ratelimitIpConfig);
}
Configuration ratelimitLoginConfig = rateLimitConfig.getSection("login");
if(ratelimitLoginConfig != null && ratelimitLoginConfig.getBoolean("enable", false)) {
ratelimitLogin = EaglerRateLimiter.loadConfig(ratelimitLoginConfig);
}
Configuration ratelimitMOTDConfig = rateLimitConfig.getSection("motd");
if(ratelimitMOTDConfig != null && ratelimitMOTDConfig.getBoolean("enable", false)) {
ratelimitMOTD = EaglerRateLimiter.loadConfig(ratelimitMOTDConfig);
}
Configuration ratelimitQueryConfig = rateLimitConfig.getSection("query");
if(ratelimitQueryConfig != null && ratelimitQueryConfig.getBoolean("enable", false)) {
ratelimitQuery = EaglerRateLimiter.loadConfig(ratelimitQueryConfig);
}
}
MOTDCacheConfiguration cacheConfig = new MOTDCacheConfiguration(cacheTTL, cacheAnimation, cacheResults,
cacheTrending, cachePortfolios);
return new EaglerListenerConfig(hostv4, hostv6, maxPlayer, tabListType, defaultServer, forceDefaultServer,
forwardIp, forwardIpHeader, redirectLegacyClientsTo, serverIcon, serverMOTD, allowMOTD, allowQuery,
minMCProtocol, maxMCProtocol, allowV3, allowV4, defragSendDelay, haproxyProtocol, cacheConfig,
httpServer, enableVoiceChat, ratelimitIp, ratelimitLogin, ratelimitMOTD, ratelimitQuery);
}
private final InetSocketAddress address;
private final InetSocketAddress addressV6;
private final int maxPlayer;
private final String tabListType;
private final String defaultServer;
private final boolean forceDefaultServer;
private final boolean forwardIp;
private final String forwardIpHeader;
private final String redirectLegacyClientsTo;
private final String serverIcon;
private final List<String> serverMOTD;
private final boolean allowMOTD;
private final boolean allowQuery;
private final int minMCProtocol;
private final int maxMCProtocol;
private final boolean allowV3;
private final boolean allowV4;
private final int defragSendDelay;
private final boolean haproxyProtocol;
private final MOTDCacheConfiguration motdCacheConfig;
private final HttpWebServer webServer;
private boolean serverIconSet = false;
private int[] serverIconPixels = null;
private final boolean enableVoiceChat;
private final EaglerRateLimiter ratelimitIp;
private final EaglerRateLimiter ratelimitLogin;
private final EaglerRateLimiter ratelimitMOTD;
private final EaglerRateLimiter ratelimitQuery;
public EaglerListenerConfig(InetSocketAddress address, InetSocketAddress addressV6, int maxPlayer,
String tabListType, String defaultServer, boolean forceDefaultServer, boolean forwardIp,
String forwardIpHeader, String redirectLegacyClientsTo, String serverIcon, List<String> serverMOTD,
boolean allowMOTD, boolean allowQuery, int minMCProtocol, int maxMCProtocol, boolean allowV3,
boolean allowV4, int defragSendDelay, boolean haproxyProtocol, MOTDCacheConfiguration motdCacheConfig,
HttpWebServer webServer, boolean enableVoiceChat, EaglerRateLimiter ratelimitIp,
EaglerRateLimiter ratelimitLogin, EaglerRateLimiter ratelimitMOTD, EaglerRateLimiter ratelimitQuery) {
super(address, String.join("\n", serverMOTD), maxPlayer, 60, Arrays.asList(defaultServer), forceDefaultServer,
Collections.emptyMap(), tabListType, false, false, 0, false, false);
this.address = address;
this.addressV6 = addressV6;
this.maxPlayer = maxPlayer;
this.tabListType = tabListType;
this.defaultServer = defaultServer;
this.forceDefaultServer = forceDefaultServer;
this.forwardIp = forwardIp;
this.forwardIpHeader = forwardIpHeader;
this.redirectLegacyClientsTo = redirectLegacyClientsTo;
this.serverIcon = serverIcon;
this.serverMOTD = serverMOTD;
this.allowMOTD = allowMOTD;
this.allowQuery = allowQuery;
this.minMCProtocol = minMCProtocol;
this.maxMCProtocol = maxMCProtocol;
this.allowV3 = allowV3;
this.allowV4 = allowV4;
this.defragSendDelay = defragSendDelay;
this.haproxyProtocol = haproxyProtocol;
this.motdCacheConfig = motdCacheConfig;
this.webServer = webServer;
this.enableVoiceChat = enableVoiceChat;
this.ratelimitIp = ratelimitIp;
this.ratelimitLogin = ratelimitLogin;
this.ratelimitMOTD = ratelimitMOTD;
this.ratelimitQuery = ratelimitQuery;
}
public InetSocketAddress getAddress() {
return address;
}
public InetSocketAddress getAddressV6() {
return addressV6;
}
public int getMaxPlayer() {
return maxPlayer;
}
public String getTabListType() {
return tabListType;
}
public String getDefaultServer() {
return defaultServer;
}
public boolean isForceDefaultServer() {
return forceDefaultServer;
}
public boolean isForwardIp() {
return forwardIp;
}
public String getForwardIpHeader() {
return forwardIpHeader;
}
public String getServerIconName() {
return serverIcon;
}
public int[] getServerIconPixels() {
if(!serverIconSet) {
if(serverIcon != null) {
File f = new File(serverIcon);
if(f.isFile()) {
serverIconPixels = ServerIconLoader.createServerIcon(f);
if(serverIconPixels == null) {
EaglerXBungee.logger().warning("Server icon could not be loaded: " + f.getAbsolutePath());
}
}else {
EaglerXBungee.logger().warning("Server icon is not a file: " + f.getAbsolutePath());
}
}
serverIconSet = true;
}
return serverIconPixels;
}
public List<String> getServerMOTD() {
return serverMOTD;
}
public boolean isAllowMOTD() {
return allowMOTD;
}
public boolean isAllowQuery() {
return allowQuery;
}
public int getMinMCProtocol() {
return minMCProtocol;
}
public int getMaxMCProtocol() {
return maxMCProtocol;
}
public boolean isAllowV3() {
return allowV3;
}
public boolean isAllowV4() {
return allowV4;
}
public int getDefragSendDelay() {
return defragSendDelay;
}
public boolean isHAProxyProtocol() {
return haproxyProtocol;
}
public HttpWebServer getWebServer() {
return webServer;
}
public MOTDCacheConfiguration getMOTDCacheConfig() {
return motdCacheConfig;
}
public boolean blockRequest(HttpRequest request) {
return false;
}
public String redirectLegacyClientsTo() {
return redirectLegacyClientsTo;
}
public boolean getEnableVoiceChat() {
return enableVoiceChat;
}
public EaglerRateLimiter getRatelimitIp() {
return ratelimitIp;
}
public EaglerRateLimiter getRatelimitLogin() {
return ratelimitLogin;
}
public EaglerRateLimiter getRatelimitMOTD() {
return ratelimitMOTD;
}
public EaglerRateLimiter getRatelimitQuery() {
return ratelimitQuery;
}
}

View File

@ -1,252 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.WeakHashMap;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketCustomizePauseMenuV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketServerInfoDataChunkV4EAG;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.PacketImageData;
import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SimpleOutputBufferImpl;
import net.md_5.bungee.config.Configuration;
public class EaglerPauseMenuConfig {
private boolean enableCustomPauseMenu;
private SPacketCustomizePauseMenuV4EAG customPauseMenuPacket;
private byte[] serverInfoHash;
private List<SPacketServerInfoDataChunkV4EAG> serverInfoChunks;
private int infoSendRate;
static EaglerPauseMenuConfig loadConfig(Configuration conf, File baseDir) throws IOException {
boolean enabled = conf.getBoolean("enable_custom_pause_menu", false);
if(!enabled) {
return new EaglerPauseMenuConfig(false, null, null, 1);
}
Configuration server_info_button = conf.getSection("server_info_button");
boolean enableInfoButton = server_info_button.getBoolean("enable_button", false);
String infoButtonText = server_info_button.getString("button_text", "Server Info");
boolean infoButtonModeNewTab = server_info_button.getBoolean("button_mode_open_new_tab", false);
String infoButtonEmbedURL = server_info_button.getString("server_info_embed_url", "");
boolean infoButtonModeEmbedFile = server_info_button.getBoolean("button_mode_embed_file", true);
String infoButtonEmbedFile = server_info_button.getString("server_info_embed_file", "server_info.html");
String infoButtonEmbedScreenTitle = server_info_button.getString("server_info_embed_screen_title", "Server Info");
int infoSendRate = server_info_button.getInt("server_info_embed_send_chunk_rate", 1);
int infoChunkSize = server_info_button.getInt("server_info_embed_send_chunk_size", 24576);
if(infoChunkSize > 32720) {
throw new IOException("Chunk size " +infoChunkSize + " is too large! Max is 32720 bytes");
}
boolean infoButtonEnableTemplateMacros = server_info_button.getBoolean("enable_template_macros", true);
Configuration globals = server_info_button.getSection("server_info_embed_template_globals");
for(String s : globals.getKeys()) {
EaglerXBungeeAPIHelper.getTemplateGlobals().put(s, globals.getString(s));
}
boolean infoButtonAllowTemplateEvalMacro = server_info_button.getBoolean("allow_embed_template_eval_macro", false);
boolean infoButtonEnableWebviewJavascript = server_info_button.getBoolean("enable_webview_javascript", false);
boolean infoButtonEnableWebviewMessageAPI = server_info_button.getBoolean("enable_webview_message_api", false);
boolean infoButtonEnableWebviewStrictCSP = server_info_button.getBoolean("enable_webview_strict_csp", true);
Configuration discord_button = conf.getSection("discord_button");
boolean enableDiscordButton = discord_button.getBoolean("enable_button", false);
String discordButtonText = discord_button.getString("button_text", "Discord");
String discordButtonURL = discord_button.getString("button_url", "https://invite url here");
int infoButtonMode = enableInfoButton
? (infoButtonModeEmbedFile
? (infoButtonEmbedFile.length() > 0
? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_WS
: SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE)
: (infoButtonEmbedURL.length() > 0
? (infoButtonModeNewTab ? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_EXTERNAL_URL
: SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP)
: SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE))
: SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE;
int discordButtonMode = (enableDiscordButton && discordButtonURL.length() > 0)
? SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_INVITE_URL
: SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_NONE;
int webviewPerms = (infoButtonEnableWebviewJavascript ? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_JAVASCRIPT : 0) |
(infoButtonEnableWebviewMessageAPI ? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_MESSAGE_API : 0) |
(infoButtonEnableWebviewStrictCSP ? SPacketCustomizePauseMenuV4EAG.SERVER_INFO_EMBED_PERMS_STRICT_CSP : 0);
Map<String,String> imagesToActuallyLoad = new WeakHashMap<>();
Configuration custom_images = conf.getSection("custom_images");
for(String s : custom_images.getKeys()) {
String fileName = custom_images.getString(s, "");
if(fileName.length() > 0) {
imagesToActuallyLoad.put(s, fileName);
}
}
Map<String,Integer> imageMappings = null;
List<PacketImageData> customImageDatas = null;
if(imagesToActuallyLoad.size() > 0) {
Map<String,PacketImageData> imageLoadingCache = new HashMap<>();
TIntObjectMap<PacketImageData> imageDumbHashTable = new TIntObjectHashMap<>();
imageMappings = new HashMap<>();
customImageDatas = new ArrayList<>();
outer_loop: for(Entry<String,String> etr : imagesToActuallyLoad.entrySet()) {
String key = etr.getKey();
String value = etr.getValue();
PacketImageData existing = imageLoadingCache.get(value);
if(existing != null) {
for(int i = 0, l = customImageDatas.size(); i < l; ++i) {
if(customImageDatas.get(i) == existing) {
imageMappings.put(key, i);
continue outer_loop;
}
}
imageMappings.put(key, customImageDatas.size());
customImageDatas.add(existing);
continue outer_loop;
}else {
PacketImageData img = EaglerXBungeeAPIHelper.loadPacketImageData(new File(baseDir, value), 64, 64);
int hashCode = Arrays.hashCode(img.rgba);
PacketImageData possibleClone = imageDumbHashTable.get(hashCode);
if (possibleClone != null && possibleClone.width == img.width && possibleClone.height == img.height
&& Arrays.equals(img.rgba, possibleClone.rgba)) {
for(int i = 0, l = customImageDatas.size(); i < l; ++i) {
if(customImageDatas.get(i) == possibleClone) {
imageMappings.put(key, i);
continue outer_loop;
}
}
imageMappings.put(key, customImageDatas.size());
customImageDatas.add(possibleClone);
continue outer_loop;
}else {
imageMappings.put(key, customImageDatas.size());
customImageDatas.add(img);
imageDumbHashTable.put(hashCode, img);
continue outer_loop;
}
}
}
}
SPacketCustomizePauseMenuV4EAG pausePacket = new SPacketCustomizePauseMenuV4EAG();
List<SPacketServerInfoDataChunkV4EAG> serverInfoChunks = null;
pausePacket.serverInfoMode = infoButtonMode;
switch(infoButtonMode) {
case SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_NONE:
default:
break;
case SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_EXTERNAL_URL:
pausePacket.serverInfoButtonText = infoButtonText;
pausePacket.serverInfoURL = infoButtonEmbedURL;
break;
case SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_HTTP:
pausePacket.serverInfoButtonText = infoButtonText;
pausePacket.serverInfoURL = infoButtonEmbedURL;
pausePacket.serverInfoEmbedPerms = webviewPerms;
pausePacket.serverInfoEmbedTitle = infoButtonEmbedScreenTitle;
break;
case SPacketCustomizePauseMenuV4EAG.SERVER_INFO_MODE_SHOW_EMBED_OVER_WS:
pausePacket.serverInfoButtonText = infoButtonText;
byte[] hash = new byte[20];
String rawData = EaglerXBungeeAPIHelper.loadFileToStringServerInfo(new File(baseDir, infoButtonEmbedFile));
if(infoButtonEnableTemplateMacros) {
rawData = EaglerXBungeeAPIHelper.loadServerInfoTemplateEagler(rawData, baseDir, infoButtonAllowTemplateEvalMacro);
}
serverInfoChunks = EaglerXBungeeAPIHelper.convertServerInfoToChunks(rawData.getBytes(StandardCharsets.UTF_8), hash, infoChunkSize);
if(!serverInfoChunks.isEmpty()) {
SPacketServerInfoDataChunkV4EAG pk = serverInfoChunks.get(0);
EaglerXBungee.logger().info("Total server info embed size: " + pk.finalSize + " bytes" + (serverInfoChunks.size() > 1 ? (" (" + serverInfoChunks.size() + " chunks)") : ""));
}
pausePacket.serverInfoEmbedPerms = webviewPerms;
pausePacket.serverInfoEmbedTitle = infoButtonEmbedScreenTitle;
pausePacket.serverInfoHash = hash;
break;
}
pausePacket.discordButtonMode = discordButtonMode;
switch(discordButtonMode) {
case SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_NONE:
default:
break;
case SPacketCustomizePauseMenuV4EAG.DISCORD_MODE_INVITE_URL:
pausePacket.discordButtonMode = discordButtonMode;
pausePacket.discordButtonText = discordButtonText;
pausePacket.discordInviteURL = discordButtonURL;
break;
}
pausePacket.imageMappings = imageMappings;
pausePacket.imageData = customImageDatas;
SimpleOutputBufferImpl ob = new SimpleOutputBufferImpl(new TestOutputStream());
pausePacket.writePacket(ob);
int cnt = ob.size();
EaglerXBungee.logger().info("Total pause menu packet size: " + cnt + " bytes");
if(cnt > 32760) {
throw new IOException("Pause menu packet is " + (cnt - 32760) + " bytes too large! Try making the images smaller or reusing the same image file for multiple icons!");
}
return new EaglerPauseMenuConfig(enabled, pausePacket, serverInfoChunks, infoSendRate);
}
private EaglerPauseMenuConfig(boolean enableCustomPauseMenu, SPacketCustomizePauseMenuV4EAG customPauseMenuPacket,
List<SPacketServerInfoDataChunkV4EAG> serverInfoChunks, int infoSendRate) {
this.enableCustomPauseMenu = enableCustomPauseMenu;
this.customPauseMenuPacket = customPauseMenuPacket;
this.serverInfoHash = customPauseMenuPacket != null ? customPauseMenuPacket.serverInfoHash : null;
this.serverInfoChunks = serverInfoChunks;
this.infoSendRate = infoSendRate;
}
public boolean getEnabled() {
return enableCustomPauseMenu;
}
public SPacketCustomizePauseMenuV4EAG getPacket() {
return customPauseMenuPacket;
}
public byte[] getServerInfoHash() {
return serverInfoHash;
}
public List<SPacketServerInfoDataChunkV4EAG> getServerInfo() {
return serverInfoChunks;
}
public int getInfoSendRate() {
return infoSendRate;
}
}

View File

@ -1,197 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
import net.md_5.bungee.config.Configuration;
public class EaglerRateLimiter {
private final int period;
private final int limit;
private final int limitLockout;
private int effectiveLimit;
private int effectiveLimitLockout;
private final int lockoutDuration;
private final List<String> exceptions;
private EaglerRateLimiter(int period, int limit, int limitLockout, int lockoutDuration, List<String> exceptions) {
this.period = period * 1000 / limit;
this.limit = this.effectiveLimit = limit;
this.limitLockout = this.effectiveLimitLockout = limitLockout;
this.lockoutDuration = lockoutDuration * 1000;
this.exceptions = exceptions;
}
public void setDivisor(int d) {
this.effectiveLimit = this.limit * d;
this.effectiveLimitLockout = this.limitLockout * d;
}
public int getPeriod() {
return period;
}
public int getLimit() {
return effectiveLimit;
}
public int getLimitLockout() {
return effectiveLimitLockout;
}
public int getLockoutDuration() {
return lockoutDuration;
}
public List<String> getExceptions() {
return exceptions;
}
public boolean isException(String addr) {
for(int i = 0, l = exceptions.size(); i < l; ++i) {
String str = exceptions.get(i);
int ll = str.length() - 1;
if(str.indexOf('*') == 0) {
if(addr.endsWith(str.substring(1))) {
return true;
}
}else if(str.lastIndexOf('*') == ll) {
if(addr.startsWith(str.substring(ll))) {
return true;
}
}else {
if(addr.equals(str)) {
return true;
}
}
}
return false;
}
protected class RateLimiter {
protected int requestCounter = 0;
protected long lockoutTimestamp = 0l;
protected long cooldownTimestamp = 0l;
protected RateLimitStatus rateLimit() {
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
tick(millis);
if(lockoutTimestamp != 0l) {
return RateLimitStatus.LOCKED_OUT;
}else {
if(++requestCounter > EaglerRateLimiter.this.effectiveLimitLockout) {
lockoutTimestamp = millis;
requestCounter = 0;
return RateLimitStatus.LIMITED_NOW_LOCKED_OUT;
}else if(requestCounter > EaglerRateLimiter.this.effectiveLimit) {
return RateLimitStatus.LIMITED;
}else {
return RateLimitStatus.OK;
}
}
}
protected void tick(long millis) {
if(lockoutTimestamp != 0l) {
if(millis - lockoutTimestamp > EaglerRateLimiter.this.lockoutDuration) {
requestCounter = 0;
lockoutTimestamp = 0l;
cooldownTimestamp = millis;
}
}else {
long delta = millis - cooldownTimestamp;
long decr = delta / EaglerRateLimiter.this.period;
if(decr >= requestCounter) {
requestCounter = 0;
cooldownTimestamp = millis;
}else {
requestCounter -= decr;
cooldownTimestamp += decr * EaglerRateLimiter.this.period;
if(requestCounter < 0) {
requestCounter = 0;
}
}
}
}
}
private final Map<String, RateLimiter> ratelimiters = new HashMap<>();
public RateLimitStatus rateLimit(String addr) {
addr = addr.toLowerCase();
if(isException(addr)) {
return RateLimitStatus.OK;
}else {
RateLimiter limiter;
synchronized(ratelimiters) {
limiter = ratelimiters.get(addr);
if(limiter == null) {
limiter = new RateLimiter();
ratelimiters.put(addr, limiter);
}
}
return limiter.rateLimit();
}
}
public void tick() {
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
synchronized(ratelimiters) {
Iterator<RateLimiter> itr = ratelimiters.values().iterator();
while(itr.hasNext()) {
RateLimiter i = itr.next();
i.tick(millis);
if(i.requestCounter <= 0 && i.lockoutTimestamp <= 0l) {
itr.remove();
}
}
}
}
public void reset() {
synchronized(ratelimiters) {
ratelimiters.clear();
}
}
static EaglerRateLimiter loadConfig(Configuration config) {
int period = config.getInt("period", -1);
int limit = config.getInt("limit", -1);
int limitLockout = config.getInt("limit_lockout", -1);
int lockoutDuration = config.getInt("lockout_duration", -1);
Collection<String> exc = (Collection<String>) config.getList("exceptions");
List<String> exceptions = new ArrayList<>();
for(String str : exc) {
exceptions.add(str.toLowerCase());
}
if(period != -1 && limit != -1 && limitLockout != -1 && lockoutDuration != -1) {
return new EaglerRateLimiter(period, limit, limitLockout, lockoutDuration, exceptions);
}else {
return null;
}
}
}

View File

@ -1,85 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import java.util.Collection;
import net.md_5.bungee.config.Configuration;
public class EaglerUpdateConfig {
static EaglerUpdateConfig loadConfig(Configuration config) {
boolean blockAllClientUpdates = config.getBoolean("block_all_client_updates", false);
boolean discardLoginPacketCerts = config.getBoolean("discard_login_packet_certs", false);
int certPacketDataRateLimit = config.getInt("cert_packet_data_rate_limit", 524288);
boolean enableEagcertFolder = config.getBoolean("enable_eagcert_folder", true);
boolean downloadLatestCerts = config.getBoolean("download_latest_certs", true);
int checkForUpdatesEvery = config.getInt("check_for_update_every", 28800);
Collection<String> downloadCertURLs = (Collection<String>)config.getList("download_certs_from");
return new EaglerUpdateConfig(blockAllClientUpdates, discardLoginPacketCerts, certPacketDataRateLimit,
enableEagcertFolder, downloadLatestCerts, checkForUpdatesEvery, downloadCertURLs);
}
private final boolean blockAllClientUpdates;
private final boolean discardLoginPacketCerts;
private final int certPacketDataRateLimit;
private final boolean enableEagcertFolder;
private final boolean downloadLatestCerts;
private final int checkForUpdatesEvery;
private final Collection<String> downloadCertURLs;
public EaglerUpdateConfig(boolean blockAllClientUpdates, boolean discardLoginPacketCerts,
int certPacketDataRateLimit, boolean enableEagcertFolder, boolean downloadLatestCerts,
int checkForUpdatesEvery, Collection<String> downloadCertURLs) {
this.blockAllClientUpdates = blockAllClientUpdates;
this.discardLoginPacketCerts = discardLoginPacketCerts;
this.certPacketDataRateLimit = certPacketDataRateLimit;
this.enableEagcertFolder = enableEagcertFolder;
this.downloadLatestCerts = downloadLatestCerts;
this.checkForUpdatesEvery = checkForUpdatesEvery;
this.downloadCertURLs = downloadCertURLs;
}
public boolean isBlockAllClientUpdates() {
return blockAllClientUpdates;
}
public boolean isDiscardLoginPacketCerts() {
return discardLoginPacketCerts;
}
public int getCertPacketDataRateLimit() {
return certPacketDataRateLimit;
}
public boolean isEnableEagcertFolder() {
return enableEagcertFolder;
}
public boolean isDownloadLatestCerts() {
return downloadLatestCerts && enableEagcertFolder;
}
public int getCheckForUpdatesEvery() {
return checkForUpdatesEvery;
}
public Collection<String> getDownloadCertURLs() {
return downloadCertURLs;
}
}

View File

@ -1,36 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
public class MOTDCacheConfiguration {
public final int cacheTTL;
public final boolean cacheServerListAnimation;
public final boolean cacheServerListResults;
public final boolean cacheServerListTrending;
public final boolean cacheServerListPortfolios;
public MOTDCacheConfiguration(int cacheTTL, boolean cacheServerListAnimation, boolean cacheServerListResults,
boolean cacheServerListTrending, boolean cacheServerListPortfolios) {
this.cacheTTL = cacheTTL;
this.cacheServerListAnimation = cacheServerListAnimation;
this.cacheServerListResults = cacheServerListResults;
this.cacheServerListTrending = cacheServerListTrending;
this.cacheServerListPortfolios = cacheServerListPortfolios;
}
}

View File

@ -1,21 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
public enum RateLimitStatus {
OK, LIMITED, LIMITED_NOW_LOCKED_OUT, LOCKED_OUT;
}

View File

@ -1,82 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import javax.imageio.ImageIO;
class ServerIconLoader {
static int[] createServerIcon(BufferedImage awtIcon) {
BufferedImage icon = awtIcon;
boolean gotScaled = false;
if(icon.getWidth() != 64 || icon.getHeight() != 64) {
icon = new BufferedImage(64, 64, awtIcon.getType());
Graphics2D g = (Graphics2D) icon.getGraphics();
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, (awtIcon.getWidth() < 64 || awtIcon.getHeight() < 64) ?
RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR : RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setBackground(new Color(0, true));
g.clearRect(0, 0, 64, 64);
int ow = awtIcon.getWidth();
int oh = awtIcon.getHeight();
int nw, nh;
float aspectRatio = (float)oh / (float)ow;
if(aspectRatio >= 1.0f) {
nw = (int)(64 / aspectRatio);
nh = 64;
}else {
nw = 64;
nh = (int)(64 * aspectRatio);
}
g.drawImage(awtIcon, (64 - nw) / 2, (64 - nh) / 2, (64 - nw) / 2 + nw, (64 - nh) / 2 + nh, 0, 0, awtIcon.getWidth(), awtIcon.getHeight(), null);
g.dispose();
gotScaled = true;
}
int[] pxls = icon.getRGB(0, 0, 64, 64, new int[4096], 0, 64);
if(gotScaled) {
for(int i = 0; i < pxls.length; ++i) {
if((pxls[i] & 0xFFFFFF) == 0) {
pxls[i] = 0;
}
}
}
return pxls;
}
static int[] createServerIcon(InputStream f) {
try {
return createServerIcon(ImageIO.read(f));
}catch(Throwable t) {
return null;
}
}
static int[] createServerIcon(File f) {
try {
return createServerIcon(ImageIO.read(f));
}catch(Throwable t) {
return null;
}
}
}

View File

@ -1,251 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import org.apache.commons.codec.binary.Base64;
import com.google.common.html.HtmlEscapers;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.repackage.lang3.StrTokenizer;
import net.md_5.bungee.BungeeCord;
public class ServerInfoTemplateParser {
private static final Gson jsonEscaper = (new GsonBuilder()).disableHtmlEscaping().create();
private static class State {
private boolean evalAllowed;
private File baseDir;
private Map<String, String> globals;
private boolean htmlEscape;
private boolean strEscape;
private boolean disableMacros;
private boolean enableEval;
private State(File baseDir, boolean evalAllowed, Map<String, String> 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, File baseDir, boolean evalAllowed, Map<String, String> 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(EaglerXBungeeAPIHelper.loadFileToByteArrayServerInfo(new File(state.baseDir, strs[2])));
case "text":
return escapeMacroResult(EaglerXBungeeAPIHelper.loadFileToStringServerInfo(new File(state.baseDir, strs[2])), state);
case "eval":
if(state.evalAllowed) {
return escapeMacroResult(loadTemplate(EaglerXBungeeAPIHelper.loadFileToStringServerInfo(new File(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 = jsonEscaper.toJson(str);
if(str.length() >= 2) {
str = str.substring(1, str.length() - 1);
}
}
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 + ")");
}
}
}

View File

@ -1,32 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import java.io.IOException;
import java.io.OutputStream;
public class TestOutputStream extends OutputStream {
@Override
public void write(int b) throws IOException {
}
@Override
public void write(byte[] b, int o, int l) throws IOException {
}
}

View File

@ -1,158 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;
import java.util.logging.Level;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.BinaryHttpClient;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.BinaryHttpClient.Response;
import net.md_5.bungee.protocol.Property;
class VanillaDefaultSkinProfileLoader implements Consumer<Response> {
private class ProfileSkinConsumerImpl implements Consumer<Response> {
private final String uuid;
private ProfileSkinConsumerImpl(String uuid) {
this.uuid = uuid;
}
@Override
public void accept(Response response) {
if(response == null) {
EaglerXBungee.logger().severe("Request error (null)");
doNotify();
}else if(response.exception != null) {
EaglerXBungee.logger().log(Level.SEVERE, "Exception loading vanilla default profile!", response.exception);
doNotify();
}else if(response.code != 200) {
EaglerXBungee.logger().severe("Recieved code " + response.code + " while looking up profile of " + uuid);
doNotify();
}else if (response.data == null) {
EaglerXBungee.logger().severe("Recieved null payload while looking up profile of " + uuid);
doNotify();
}else {
try {
JsonObject json = JsonParser.parseString(new String(response.data, StandardCharsets.UTF_8)).getAsJsonObject();
JsonElement propsElement = json.get("properties");
if(propsElement != null) {
JsonArray properties = propsElement.getAsJsonArray();
if(properties.size() > 0) {
for(int i = 0, l = properties.size(); i < l; ++i) {
JsonElement prop = properties.get(i);
if(prop.isJsonObject()) {
JsonObject propObj = prop.getAsJsonObject();
if(propObj.get("name").getAsString().equals("textures")) {
JsonElement value = propObj.get("value");
JsonElement signature = propObj.get("signature");
if(value != null) {
Property newProp = new Property("textures", value.getAsString(),
signature != null ? signature.getAsString() : null);
config.eaglerPlayersVanillaSkinCached = new Property[] { newProp, EaglerBungeeConfig.isEaglerProperty };
}
EaglerXBungee.logger().info("Loaded vanilla profile: " + config.getEaglerPlayersVanillaSkin());
doNotify();
return;
}
}
}
}
}
EaglerXBungee.logger().warning("No skin was found for: " + config.getEaglerPlayersVanillaSkin());
}catch(Throwable t) {
EaglerXBungee.logger().log(Level.SEVERE, "Exception processing name to UUID lookup response!", t);
}
doNotify();
}
}
}
private final EaglerBungeeConfig config;
private volatile boolean isLocked = true;
private final Object lock = new Object();
private VanillaDefaultSkinProfileLoader(EaglerBungeeConfig config) {
this.config = config;
}
@Override
public void accept(Response response) {
if(response == null) {
EaglerXBungee.logger().severe("Request error (null)");
doNotify();
}else if(response.exception != null) {
EaglerXBungee.logger().log(Level.SEVERE, "Exception loading vanilla default profile!", response.exception);
doNotify();
}else if(response.code != 200) {
EaglerXBungee.logger().severe("Recieved code " + response.code + " while looking up UUID of " + config.getEaglerPlayersVanillaSkin());
doNotify();
}else if (response.data == null) {
EaglerXBungee.logger().severe("Recieved null payload while looking up UUID of " + config.getEaglerPlayersVanillaSkin());
doNotify();
}else {
try {
JsonObject json = JsonParser.parseString(new String(response.data, StandardCharsets.UTF_8)).getAsJsonObject();
String uuid = json.get("id").getAsString();
URI requestURI = URI.create("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false");
BinaryHttpClient.asyncRequest("GET", requestURI, new ProfileSkinConsumerImpl(uuid));
}catch(Throwable t) {
EaglerXBungee.logger().log(Level.SEVERE, "Exception processing name to UUID lookup response!", t);
doNotify();
}
}
}
private void doNotify() {
synchronized(lock) {
if(isLocked) {
isLocked = false;
lock.notify();
}
}
}
static void lookupVanillaSkinUser(EaglerBungeeConfig config) {
String user = config.getEaglerPlayersVanillaSkin();
EaglerXBungee.logger().info("Loading vanilla profile: " + user);
URI requestURI = URI.create("https://api.mojang.com/users/profiles/minecraft/" + user);
VanillaDefaultSkinProfileLoader loader = new VanillaDefaultSkinProfileLoader(config);
synchronized(loader.lock) {
BinaryHttpClient.asyncRequest("GET", requestURI, loader);
if(loader.isLocked) {
try {
loader.lock.wait(5000l);
} catch (InterruptedException e) {
}
if(loader.isLocked) {
EaglerXBungee.logger().warning("Profile load timed out");
}
}
}
}
}

View File

@ -1,209 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.handlers;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
import java.util.logging.Level;
import org.apache.commons.codec.binary.Base64;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import net.lax1dude.eaglercraft.v1_8.plugin.backend_rpc_protocol.EaglerBackendRPCProtocol;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.DefaultAuthSystem;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerAuthConfig;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerPipeline;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.backend_rpc_protocol.BackendRPCSessionHandler;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.protocol.GameProtocolMessageController;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinPackets;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.api.connection.Server;
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
import net.md_5.bungee.api.event.PluginMessageEvent;
import net.md_5.bungee.api.event.PostLoginEvent;
import net.md_5.bungee.api.event.ServerConnectedEvent;
import net.md_5.bungee.api.event.ServerDisconnectEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.event.EventHandler;
import net.md_5.bungee.protocol.Property;
public class EaglerPacketEventListener implements Listener {
public static final String GET_DOMAIN_CHANNEL = "EAG|GetDomain";
public final EaglerXBungee plugin;
public EaglerPacketEventListener(EaglerXBungee plugin) {
this.plugin = plugin;
}
@EventHandler
public void onPluginMessage(final PluginMessageEvent event) {
if(event.getSender() instanceof UserConnection) {
final UserConnection player = (UserConnection)event.getSender();
String tag = event.getTag();
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler)player.getPendingConnection();
GameProtocolMessageController msgController = initialHandler.getEaglerMessageController();
if(msgController != null) {
try {
if(msgController.handlePacket(tag, event.getData())) {
event.setCancelled(true);
return;
}
} catch (Throwable e) {
event.getSender().disconnect(new TextComponent("Eaglercraft packet error!"));
event.setCancelled(true);
return;
}
}
}
if(tag.equals(EaglerBackendRPCProtocol.CHANNEL_NAME) || tag.equals(EaglerBackendRPCProtocol.CHANNEL_NAME_MODERN)) {
event.getSender().disconnect(new TextComponent("Nope!"));
event.setCancelled(true);
return;
}
if(tag.equals(EaglerBackendRPCProtocol.CHANNEL_NAME_READY) || tag.equals(EaglerBackendRPCProtocol.CHANNEL_NAME_READY_MODERN)) {
event.setCancelled(true);
return;
}
}else if(event.getSender() instanceof Server && event.getReceiver() instanceof UserConnection) {
UserConnection player = (UserConnection)event.getReceiver();
String tag = event.getTag();
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler)player.getPendingConnection();
if(EaglerBackendRPCProtocol.CHANNEL_NAME.equals(tag) || tag.equals(EaglerBackendRPCProtocol.CHANNEL_NAME_MODERN)) {
event.setCancelled(true);
try {
initialHandler.handleBackendRPCPacket((Server)event.getSender(), event.getData());
}catch(Throwable t) {
EaglerXBungee.logger().log(Level.SEVERE, "[" + ((UserConnection) event.getReceiver()).getName()
+ "]: Caught an exception handling backend RPC packet!", t);
}
}else if(GET_DOMAIN_CHANNEL.equals(tag)) {
event.setCancelled(true);
String domain = initialHandler.getOrigin();
if(domain != null) {
((Server)event.getSender()).sendData("EAG|Domain", domain.getBytes(StandardCharsets.UTF_8));
}else {
((Server)event.getSender()).sendData("EAG|Domain", new byte[] { 0 });
}
}
}else {
if(EaglerBackendRPCProtocol.CHANNEL_NAME.equals(tag) || tag.equals(EaglerBackendRPCProtocol.CHANNEL_NAME_MODERN)) {
event.setCancelled(true);
try {
BackendRPCSessionHandler.handlePacketOnVanilla((Server) event.getSender(),
(UserConnection) event.getReceiver(), event.getData());
}catch(Throwable t) {
EaglerXBungee.logger().log(Level.SEVERE, "[" + ((UserConnection) event.getReceiver()).getName()
+ "]: Caught an exception handling backend RPC packet!", t);
}
}
}
}
}
@EventHandler
public void onPostLogin(PostLoginEvent event) {
ProxiedPlayer p = event.getPlayer();
if(p instanceof UserConnection) {
UserConnection player = (UserConnection)p;
InitialHandler handler = player.getPendingConnection();
LoginResult res = handler.getLoginProfile();
if(res != null) {
Property[] props = res.getProperties();
if(props.length > 0) {
for(int i = 0; i < props.length; ++i) {
Property pp = props[i];
if(pp.getName().equals("textures")) {
try {
String jsonStr = SkinPackets.bytesToAscii(Base64.decodeBase64(pp.getValue()));
JsonObject json = JsonParser.parseString(jsonStr).getAsJsonObject();
JsonObject skinObj = json.getAsJsonObject("SKIN");
if(skinObj != null) {
JsonElement url = json.get("url");
if(url != null) {
String urlStr = SkinService.sanitizeTextureURL(url.getAsString());
plugin.getSkinService().registerTextureToPlayerAssociation(urlStr, player.getUniqueId());
}
}
}catch(Throwable t) {
}
}
}
}
}
EaglerAuthConfig authConf = plugin.getConfig().getAuthConfig();
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
DefaultAuthSystem srv = plugin.getAuthService();
if(srv != null) {
srv.handleVanillaLogin(event);
}
}
}
}
@EventHandler
public void onConnectionLost(PlayerDisconnectEvent event) {
UUID uuid = event.getPlayer().getUniqueId();
plugin.getSkinService().unregisterPlayer(uuid);
plugin.getCapeService().unregisterPlayer(uuid);
if(event.getPlayer() instanceof UserConnection) {
UserConnection player = (UserConnection)event.getPlayer();
if((player.getPendingConnection() instanceof EaglerInitialHandler)
&& ((EaglerInitialHandler) player.getPendingConnection()).getEaglerListenerConfig()
.getEnableVoiceChat()) {
plugin.getVoiceService().handlePlayerLoggedOut(player);
}
}
}
@EventHandler
public void onServerConnected(ServerConnectedEvent event) {
if(event.getPlayer() instanceof UserConnection) {
EaglerPipeline.addServerConnectListener((UserConnection)event.getPlayer());
}
}
@EventHandler
public void onServerDisconnected(ServerDisconnectEvent event) {
if(event.getPlayer() instanceof UserConnection) {
UserConnection player = (UserConnection)event.getPlayer();
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
EaglerInitialHandler handler = (EaglerInitialHandler) player.getPendingConnection();
BackendRPCSessionHandler rpcHandler = handler.getRPCSessionHandler();
if(rpcHandler != null) {
rpcHandler.handleConnectionLost(event.getTarget());
}
if(handler.getEaglerListenerConfig().getEnableVoiceChat()) {
plugin.getVoiceService().handleServerDisconnected(player, event.getTarget());
}
}
}
}
}

View File

@ -1,37 +0,0 @@
/*
* 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.
*
*/
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.handlers;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
import net.md_5.bungee.api.event.ProxyReloadEvent;
import net.md_5.bungee.api.plugin.Listener;
import net.md_5.bungee.event.EventHandler;
public class EaglerPluginEventListener implements Listener {
public final EaglerXBungee plugin;
public EaglerPluginEventListener(EaglerXBungee plugin) {
this.plugin = plugin;
}
@EventHandler
public void onReload(ProxyReloadEvent evt) {
plugin.reload();
}
}

Some files were not shown because too many files have changed in this diff Show More