Updated README, icon_flipX, icon_flipY, icon_rotate

This commit is contained in:
LAX1DUDE 2022-04-02 17:14:07 -07:00
parent 05c903b0ed
commit cf6ff48277
7 changed files with 282 additions and 23 deletions

Binary file not shown.

137
README.md
View File

@ -4,7 +4,7 @@
### This plugin can add animated MOTDs to your Eaglercraft server ### This plugin can add animated MOTDs to your Eaglercraft server
![EaglerMOTD Sample](https://i.gyazo.com/ec23a9c60e9722209246fc2b2acea8e4.gif) ![EaglerMOTD Sample](https://i.gyazo.com/4e0105720c866990c82b221fe82f7cc9.gif)
**It can also add custom "Accept:" query handlers for 3rd party sites to gather more information about your server** **It can also add custom "Accept:" query handlers for 3rd party sites to gather more information about your server**
@ -16,7 +16,140 @@ You will find a new 'EaglerMOTD' folder in the plugins folder you put the jar in
## Configuration Guide ## Configuration Guide
Just a minute... ### Messages.json:
```json
{
"close_socket_after": 1200,
"max_sockets_per_ip": 10,
"max_total_sockets": 256,
"allow_banned_ips": false,
"messages": {
"all": [
{
"name": "default",
"frames": [
"frames.frame1",
"frames.frame2",
"frames.frame3",
"frames.frame4"
],
"interval": 8,
"random": false,
"shuffle": false,
"timeout": 500,
"weight": 1.0,
"next": "any"
}
]
}
}
```
- `close_socket_after` Defines the maximum number of ticks (1/20ths of a second) that an animated MOTD should be displayed to a player (default 1200 ticks, which is 60 seconds)
- `max_sockets_per_ip` Defines the maximum number of MOTD connections that a single IP address can open (does not apply to local IPs like 127.0.0.1 and 192.168.0.0 have no limit)
- `max_total_sockets` Defines the maximum number of MOTD connections that can be open on the server at any given time
- `allow_banned_ips` Defines if animated MOTDs should play for banned IPs
- `messages` Defines the list of possible messages to display on every listener
- `all` Defines the list of possible messages to display on all listeners
- To define a list for a specific listener, type the hostname of the listener. The default host defined in a fresh default `config.yml` in EaglercraftBungee is `0.0.0.0:25565` so to define a list of messages for that specific listener you would use `"0.0.0.0:25565"` instead of `"all"` to add the messages. Messages defined as `"all"` will be added to the list of messages for all listeners.
- The list contains a set of JSON objects each containing these fields:
- `name` *(Optional)* Defines the name of the message, used for specifying a `next` property in a different message to jump to this message once it has timed out
- `frames` Defines a list of strings specifying the list of frames in the animation of this message. They are defined in `file.frame` format where `file` is the file the frame is located in (which would be `file.json`) and `frame` is the name of the frame in that file to use
- `interval` *(Optional)* defines the delay between frames. Setting to `0` (default) disables the animation and only shows the first frame
- `random` *(Optional)* defines if the animation should begin on a random frame (`true`) or the first frame (`false`, default)
- `shuffle` *(Optional)* defines if the animation should switch between frames sequentially (`false`, default) or randomly (`true`)
- `timeout` *(Optional)* defines how many ticks the animation should play before stopping or switching to `"next"` (default 500)
- `weight` *(Optional)* defines the random probability of choosing this message over any other messages in `"all"` and the list of messages for the specific listener the message is playing on. Default is 1.0
- `next` *(Optional)* defines the `"name"` of the message to play once this message times out (`timeout`). Set to `"any"` to pick any message, or set to `null` to stop the animation once `timeout` is reached. Default is null
### Frames.json:
**You can name this file anything you want, and you can create more than one. In 'messages.json' just define the frame as, for example, 'file.name' and it will look in a file called 'file.json' for a frame named 'name'. The example 'frames.frame1' in 'messages.json' will load 'frame1' from this default file called 'frames.json'**
```json
{
"frame1": {
"icon": "server-animation.png",
"icon_spriteX": 0,
"icon_spriteY": 0,
"online": "default",
"max": "default",
"players": "default",
"text0": "&7An Eaglercraft server",
"text1": "&0!!!!&8Running EaglerMOTD plugin"
},
"frame2": {
"icon": "server-animation.png",
"icon_spriteX": 1,
"icon_spriteY": 0,
"icon_color": [ 1.0, 0.0, 0.0, 0.15 ],
"online": 10,
"players": [ "fake player 1", "fake player 2" ],
"text0": "&6&nAn&r &7Eaglercraft server",
"text1": "&0!!&8!&0!&8Running EaglerMOTD plugin"
},
"frame3": {
"icon": "server-animation.png",
"icon_spriteX": 2,
"icon_spriteY": 0,
"icon_color": [ 1.0, 0.0, 0.0, 0.15 ],
"icon_tint": [ 0.8, 0.8, 1.0 ],
"online": 20,
"players": [],
"text0": "&7An &6&nEaglercraft&r &7server",
"text1": "&0!&8!!&0!&8Running EaglerMOTD plugin"
},
"frame4": {
"icon": "server-animation.png",
"icon_spriteX": 3,
"icon_spriteY": 0,
"icon_color": [ 1.0, 1.0, 0.0, 0.15 ],
"icon_tint": [ 0.8, 0.8, 1.0, 0.8 ],
"online": 30,
"players": "default",
"text0": "&7An Eaglercraft &6&nserver&r",
"text1": "&8!!!&0!&8Running EaglerMOTD plugin"
}
}
```
**Every frame will retrieve the values from the previous frame in the message for the default value for every variable**
- `text0` *(Optional)* Changes the first line of text in the server's current MOTD
- `text1` *(Optional)* Changes the second line of text in the server's current MOTD
- `online` *(Optional)* Changes the number of online players, use `"default"` to reset it
- `max` *(Optional)* Changes the max number of players in the MOTD, use `"default"` to reset it
- `players` *(Optional)* Changes the list of players shown when the mouse is hovering over the online/max count in the multiplayer screen. use `"default"` to reset it and show the real list of players instead of spoofing it
- `icon` *(Optional)* A JPEG/PNG/BMP/GIF to display as the server icon. The icon must be at least 64x64 pixels. **Transparency is supported.** The top left 64x64 pixels of the image are displayed if the image is larger than 64x64 pixels. **Animated GIF files are not supported, they load but only display the first frame.** Setting the icon resets the values of `icon_spriteX`, `icon_spriteY`, `icon_color`, `icon_tint`, `icon_flipX`, `icon_flipY`, and `icon_rotate` **to their default values.** Setting to `"none"` will reset everything and set the icon to be black and 100% transparent
- `icon_spriteX` *(Optional)* defines the X coordinate to read a 64x64 pixel portion of the current `"icon"` file, if the file is larger than 64x64. The value is multiplied by 64 to get the exact pixel coordinate in the image to read from. `"icon_spriteX": 2` will read a 64x64 pixel portion of a larger image beginning at 128 pixels X and 0 pixels Y of the current `"icon"` file. Default is 0
- `icon_spriteY` *(Optional)* defines the Y coordinate to read a 64x64 pixel portion of the current `"icon"` file, if the file is larger than 64x64. The value is multiplied by 64 to get the exact pixel coordinate in the image to read from. `"icon_spriteY": 2` will read a 64x64 pixel portion of a larger image beginning at 0 pixels X and 128 pixels Y of the current `"icon"` file. Default is 0. Setting, for example, `"icon_spriteX": 1` and `"icon_spriteY": 2` will read a 64x64 pixel portion of the current `"icon"` file beginning at 64 pixels X and 128 pixels Y.
- `icon_pixelX` *(Optional)* defines the exact X pixel coordinate to read a 64x64 pixel portion of the current `"icon"` file, if the file is larger than 64x64. Unlike `icon_spriteX`, setting this value will not multiply the input value by 64, it will read from the exact coordinate in the image. **Overrides icon_spriteX**
- `icon_pixelY` *(Optional)* defines the exact Y pixel coordinate to read a 64x64 pixel portion of the current `"icon"` file, if the file is larger than 64x64. Unlike `icon_spriteY`, setting this value will not multiply the input value by 64, it will read from the exact coordinate in the image. **Overrides icon_spriteY**
- `icon_color` *(Optional)* mixes an RGBA color with the current `"icon"`, or displays the color directly if `"icon"` is `"none"`. `[1.0, 1.0, 1.0]` displays white, `[0.0, 0.0, 0.0]` displays black, `[0.0, 1.0, 0.0]` displays green, `[0.0, 1.0, 0.0, 0.5]` displays green with 50% transparency, blending it with the color of the current `"icon"` file's pixels if it is set.
- `icon_tint` *(Optional)* multiplies an RGBA color by all the pixels in the current `"icon"` and/or `"icon_color"`. `[0.0, 0.0, 1.0]` would make the icon's pixels blue-colored, `[1.0, 0.0, 0.0]` would make the icon's pixels red-colored, `[0.0, 1.0, 0.0, 0.5]` would make the icon's pixels green-colored and 50% transparent. `[2.0, 2.0, 2.0]` would double the brightness of the icon.
- `icon_flipX` *(Optional)* flips the pixels of the icon displayed horizontally
- `icon_flipY` *(Optional)* flips the pixels of the icon displayed vertically
- `icon_rotate` *(Optional)* rotates the icon 90°, 180°, or 270° clockwise (`0` = 0°, `1` = 90°, `2` = 180°, `3` = 270°)
## Compiling and Contributing ## Compiling and Contributing

View File

@ -6,37 +6,39 @@
"online": "default", "online": "default",
"max": "default", "max": "default",
"players": "default", "players": "default",
"text0": "&6An Eaglercraft server", "text0": "&7An Eaglercraft server",
"text1": "&7Running EaglerMOTD plugin" "text1": "&0!!!!&8Running EaglerMOTD plugin"
}, },
"frame2": { "frame2": {
"icon": "server-animation.png", "icon": "server-animation.png",
"icon_spriteX": 1, "icon_spriteX": 1,
"icon_spriteY": 0, "icon_spriteY": 0,
"icon_color": [ 1.0, 0.0, 0.0, 0.2 ], "icon_color": [ 1.0, 0.0, 0.0, 0.15 ],
"online": 10, "online": 10,
"players": [ "fake player 1", "fake player 2" ], "players": [ "fake player 1", "fake player 2" ],
"text0": "&bAn &6Eaglercraft server" "text0": "&6&nAn&r &7Eaglercraft server",
"text1": "&0!!&8!&0!&8Running EaglerMOTD plugin"
}, },
"frame3": { "frame3": {
"icon": "server-animation.png", "icon": "server-animation.png",
"icon_spriteX": 2, "icon_spriteX": 2,
"icon_spriteY": 0, "icon_spriteY": 0,
"icon_tint": [ 0.6, 0.6, 1.0, 0.8 ], "icon_color": [ 1.0, 0.0, 0.0, 0.15 ],
"icon_tint": [ 0.8, 0.8, 1.0 ],
"online": 20, "online": 20,
"players": [], "players": [],
"text0": "&6An &bEaglercraft &6server" "text0": "&7An &6&nEaglercraft&r &7server",
"text1": "&0!&8!!&0!&8Running EaglerMOTD plugin"
}, },
"frame4": { "frame4": {
"icon": "server-animation.png", "icon": "server-animation.png",
"icon_spriteX": 3, "icon_spriteX": 3,
"icon_spriteY": 0, "icon_spriteY": 0,
"icon_color": [ 1.0, 1.0, 0.0, 0.2 ], "icon_color": [ 1.0, 1.0, 0.0, 0.15 ],
"icon_tint": [ 0.5, 0.5, 1.0 ], "icon_tint": [ 0.8, 0.8, 1.0, 0.8 ],
"online": 30, "online": 30,
"max": 69,
"players": "default", "players": "default",
"text0": "&6An Eaglercraft &bserver", "text0": "&7An Eaglercraft &6&nserver&r",
"text1": "&7Running &k&oEaglerMOTD&r&7 plugin" "text1": "&8!!!&0!&8Running EaglerMOTD plugin"
} }
} }

View File

@ -58,8 +58,8 @@ public class BitmapFile {
if(x < 0 || y < 0) { if(x < 0 || y < 0) {
return null; return null;
} }
int offsetX = x * 64; int offsetX = x;
int offsetY = y * 64; int offsetY = y;
if(offsetX + 64 > w || offsetY + 64 > h) { if(offsetX + 64 > w || offsetY + 64 > h) {
return null; return null;
} }
@ -73,6 +73,14 @@ public class BitmapFile {
} }
public static int[] makeColor(int[] in, float r, float g, float b, float a) { public static int[] makeColor(int[] in, float r, float g, float b, float a) {
if(r < 0.0f) r = 0.0f;
if(r > 1.0f) r = 1.0f;
if(g < 0.0f) g = 0.0f;
if(g > 1.0f) g = 1.0f;
if(b < 0.0f) b = 0.0f;
if(b > 1.0f) b = 1.0f;
if(a < 0.0f) a = 0.0f;
if(a > 1.0f) a = 1.0f;
int c = ((int)(a*255.0f) << 24) | ((int)(r*255.0f) << 16) | ((int)(g*255.0f) << 8) | (int)(b*255.0f); int c = ((int)(a*255.0f) << 24) | ((int)(r*255.0f) << 16) | ((int)(g*255.0f) << 8) | (int)(b*255.0f);
for(int i = 0; i < in.length; ++i) { for(int i = 0; i < in.length; ++i) {
in[i] = c; in[i] = c;
@ -90,6 +98,14 @@ public class BitmapFile {
gg = g * a + gg * (1.0f - a); gg = g * a + gg * (1.0f - a);
bb = b * a + bb * (1.0f - a); bb = b * a + bb * (1.0f - a);
aa = a + aa * (1.0f - a); aa = a + aa * (1.0f - a);
if(rr < 0.0f) rr = 0.0f;
if(rr > 1.0f) rr = 1.0f;
if(gg < 0.0f) gg = 0.0f;
if(gg > 1.0f) gg = 1.0f;
if(bb < 0.0f) bb = 0.0f;
if(bb > 1.0f) bb = 1.0f;
if(aa < 0.0f) aa = 0.0f;
if(aa > 1.0f) aa = 1.0f;
in[i] = ((int)(aa*255.0f) << 24) | ((int)(rr*255.0f) << 16) | ((int)(gg*255.0f) << 8) | (int)(bb*255.0f); in[i] = ((int)(aa*255.0f) << 24) | ((int)(rr*255.0f) << 16) | ((int)(gg*255.0f) << 8) | (int)(bb*255.0f);
} }
return in; return in;
@ -101,9 +117,64 @@ public class BitmapFile {
float gg = ((in[i] >> 8) & 0xFF) / 255.0f * g; float gg = ((in[i] >> 8) & 0xFF) / 255.0f * g;
float bb = (in[i] & 0xFF) / 255.0f * b; float bb = (in[i] & 0xFF) / 255.0f * b;
float aa = ((in[i] >> 24) & 0xFF) / 255.0f * a; float aa = ((in[i] >> 24) & 0xFF) / 255.0f * a;
if(rr < 0.0f) rr = 0.0f;
if(rr > 1.0f) rr = 1.0f;
if(gg < 0.0f) gg = 0.0f;
if(gg > 1.0f) gg = 1.0f;
if(bb < 0.0f) bb = 0.0f;
if(bb > 1.0f) bb = 1.0f;
if(aa < 0.0f) aa = 0.0f;
if(aa > 1.0f) aa = 1.0f;
in[i] = ((int)(aa*255.0f) << 24) | ((int)(rr*255.0f) << 16) | ((int)(gg*255.0f) << 8) | (int)(bb*255.0f); in[i] = ((int)(aa*255.0f) << 24) | ((int)(rr*255.0f) << 16) | ((int)(gg*255.0f) << 8) | (int)(bb*255.0f);
} }
return in; return in;
} }
private static final int[] flipTmpBuffer = new int[64];
public static int[] flipX(int[] newIcon) {
for(int y = 0; y < 64; ++y) {
int o = y * 64;
System.arraycopy(newIcon, o, flipTmpBuffer, 0, 64);
for(int i = 0; i < 64; ++i) {
newIcon[o + i] = flipTmpBuffer[63 - i];
}
}
return newIcon;
}
public static int[] flipY(int[] newIcon) {
for(int x = 0; x < 64; ++x) {
for(int i = 0; i < 64; ++i) {
flipTmpBuffer[i] = newIcon[i * 64 + x];
}
for(int i = 0; i < 64; ++i) {
newIcon[i * 64 + x] = flipTmpBuffer[63 - i];
}
}
return newIcon;
}
private static int[] transpose(int[] in) {
int[] ret = new int[64*64];
for(int y = 0; y < 64; ++y) {
for(int x = 0; x < 64; ++x) {
ret[x * 64 + y] = in[y * 64 + x];
}
}
return ret;
}
public static int[] rotate(int[] newIcon, int rotate) {
rotate = rotate % 4;
if(rotate == 1) {
newIcon = flipX(transpose(newIcon));
}else if(rotate == 2) {
newIcon = flipY(flipX(newIcon));
}else if(rotate == 3) {
newIcon = flipY(transpose(newIcon));
}
return newIcon;
}
} }

View File

@ -29,6 +29,9 @@ public class MOTDConnection {
public BitmapFile bitmap = null; public BitmapFile bitmap = null;
public int spriteX = 0; public int spriteX = 0;
public int spriteY = 0; public int spriteY = 0;
public boolean flipX = false;
public boolean flipY = false;
public int rotate = 0;
public float[] color = new float[] { 0.0f, 0.0f, 0.0f, 0.0f }; public float[] color = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };
public float[] tint = new float[] { 0.0f, 0.0f, 0.0f, 0.0f }; public float[] tint = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };
@ -158,6 +161,9 @@ public class MOTDConnection {
} }
private boolean changeMessageTo(String group, String s) { private boolean changeMessageTo(String group, String s) {
if(group == null || s == null) {
return false;
}
List<MessagePoolEntry> lst = EaglerMOTD.messages.get(group); List<MessagePoolEntry> lst = EaglerMOTD.messages.get(group);
if(lst == null) { if(lst == null) {
return false; return false;
@ -230,28 +236,64 @@ public class MOTDConnection {
shouldPush = true; shouldPush = true;
} }
boolean shouldRenderIcon = false; boolean shouldRenderIcon = false;
String icon = frame.optString("icon", null); Object icon = frame.opt("icon");
if(icon != null) { if(icon != null) {
String asString = (icon instanceof String) ? (String)icon : null;
shouldRenderIcon = true; shouldRenderIcon = true;
if(icon.equalsIgnoreCase("none") || icon.equalsIgnoreCase("default") || icon.equalsIgnoreCase("null") || icon.equalsIgnoreCase("color")) { if(icon == JSONObject.NULL || asString == null || asString.equalsIgnoreCase("none") || asString.equalsIgnoreCase("default")
|| asString.equalsIgnoreCase("null") || asString.equalsIgnoreCase("color")) {
bitmap = null; bitmap = null;
}else { }else {
bitmap = BitmapFile.getCachedIcon(icon); bitmap = BitmapFile.getCachedIcon(asString);
} }
spriteX = spriteY = 0; spriteX = spriteY = rotate = 0;
flipX = flipY = false;
color = new float[] { 0.0f, 0.0f, 0.0f, 0.0f }; color = new float[] { 0.0f, 0.0f, 0.0f, 0.0f };
tint = new float[] { 1.0f, 1.0f, 1.0f, 1.0f }; tint = new float[] { 1.0f, 1.0f, 1.0f, 1.0f };
} }
int sprtX = frame.optInt("icon_spriteX", -1); int sprtX = frame.optInt("icon_spriteX", -1) * 64;
if(sprtX >= 0 && sprtX != spriteX) { if(sprtX >= 0 && sprtX != spriteX) {
shouldRenderIcon = true; shouldRenderIcon = true;
spriteX = sprtX; spriteX = sprtX;
} }
int sprtY = frame.optInt("icon_spriteY", -1); int sprtY = frame.optInt("icon_spriteY", -1) * 64;
if(sprtY >= 0 && sprtY != spriteY) { if(sprtY >= 0 && sprtY != spriteY) {
shouldRenderIcon = true; shouldRenderIcon = true;
spriteY = sprtY; spriteY = sprtY;
} }
sprtX = frame.optInt("icon_pixelX", -1);
if(sprtX >= 0 && sprtX != spriteX) {
shouldRenderIcon = true;
spriteX = sprtX;
}
sprtY = frame.optInt("icon_pixelY", -1);
if(sprtY >= 0 && sprtY != spriteY) {
shouldRenderIcon = true;
spriteY = sprtY;
}
Object flip = frame.opt("icon_flipX");
if(flip != null) {
shouldRenderIcon = true;
if(flip instanceof Boolean) {
flipX = ((Boolean)flip).booleanValue();
}else {
flipX = false;
}
}
flip = frame.opt("icon_flipY");
if(flip != null) {
shouldRenderIcon = true;
if(flip instanceof Boolean) {
flipY = ((Boolean)flip).booleanValue();
}else {
flipY = false;
}
}
int rot = frame.optInt("icon_rotate", -1);
if(rot >= 0) {
shouldRenderIcon = true;
rotate = rot % 4;
}
JSONArray colorF = frame.optJSONArray("icon_color"); JSONArray colorF = frame.optJSONArray("icon_color");
if(colorF != null && colorF.length() > 0) { if(colorF != null && colorF.length() > 0) {
shouldRenderIcon = true; shouldRenderIcon = true;
@ -271,7 +313,7 @@ public class MOTDConnection {
if(shouldRenderIcon) { if(shouldRenderIcon) {
int[] newIcon = null; int[] newIcon = null;
if(bitmap != null) { if(bitmap != null) {
newIcon = bitmap.getSprite(sprtX, sprtY); newIcon = bitmap.getSprite(spriteX, spriteY);
} }
if(newIcon == null) { if(newIcon == null) {
newIcon = new int[64*64]; newIcon = new int[64*64];
@ -280,6 +322,17 @@ public class MOTDConnection {
if(color[3] > 0.0f) { if(color[3] > 0.0f) {
newIcon = BitmapFile.applyColor(newIcon, color[0], color[1], color[2], color[3]); newIcon = BitmapFile.applyColor(newIcon, color[0], color[1], color[2], color[3]);
} }
if(bitmap != null) {
if(flipX) {
newIcon = BitmapFile.flipX(newIcon);
}
if(flipY) {
newIcon = BitmapFile.flipY(newIcon);
}
if(rotate != 0) {
newIcon = BitmapFile.rotate(newIcon, rotate);
}
}
motd.setBitmap(newIcon); motd.setBitmap(newIcon);
shouldPush = true; shouldPush = true;
} }

View File

@ -1,4 +1,4 @@
name: EaglerMOTD name: EaglerMOTD
main: net.lax1dude.eaglercraft.eaglermotd.EaglerMOTD main: net.lax1dude.eaglercraft.eaglermotd.EaglerMOTD
version: 1.0.2 version: 1.0.3
author: LAX1DUDE author: LAX1DUDE

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 35 KiB