Skip to content

Commit

Permalink
The 3.0.0 Update!
Browse files Browse the repository at this point in the history
- Improved performance by using "recent" additions from Placeholder API!
- Added support for checking against predicates (via Predicate API). This allows for more flexible player name formatting and style access.
- Added support for simple animations/switching headers and footers. This allows you to periodically change what is displayed.
- Added ability to disable custom player name formatting per style styles, which preserves vanilla look. This can be used for better conditional mod compatibility.
- Added ability to hide player names on Player List. This doesn't effect commands, suggestions or entity visiblity!
- Update time is now defined per style, instead of global config.
- Cleaned up config format. Should be nicer to use.
- New icon! Using similar style to Styled Sidebar one.
  • Loading branch information
Patbox committed May 25, 2023
1 parent 26b368c commit 8329e8b
Show file tree
Hide file tree
Showing 29 changed files with 960 additions and 177 deletions.
108 changes: 73 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
![Logo](https://i.imgur.com/MV1mbYv.png)
![Logo](https://i.imgur.com/DOl47Dn.png)

# Styled Player List
It's a simple mod that allows server owners to style their player list as they like!
With full permission support, placeholder api support, multiple styles and player name overrides.
With full permission/requirement support, placeholder api support, multiple styles and player list name overrides.

*This mod works only on Fabric Mod Loader and compatible!*
*This mod works only on Fabric and Quilt!*

If you have any questions, you can ask them on my [Discord](https://pb4.eu/discord)

Expand All @@ -21,48 +21,86 @@ If you have any questions, you can ask them on my [Discord](https://pb4.eu/disco

## Configuration:
You can find config file in `./config/styledplayerlist/`.
Some config options allow for dynamic predicates (marked as `{/* PREDICATE */}`).
See [this page](https://github.com/Patbox/PredicateAPI/blob/1.19.4/BUILTIN.md) for more details.
[Formatting uses PlaceholderAPI's Text Parser for which docs you can find here](https://placeholders.pb4.eu/user/text-format/).

```json5
{
"defaultStyle": "default", // allows to select id of default player list
"updateRate": 20, // change how often player list is updated (20 = every 1 second)
"...Message": "...", // allows to change messages
"changePlayerName": false, // if true, names of players on player list will be changed
"playerNameFormat": "%player:display_name%", // format of player name (uses Text Parser and placeholders)
"updatePlayerNameEveryChatMessage": false, // if true, everytime player sends a message, theirs name will be updated
"playerNameUpdateRate": -1 , // changes how often player name is updated (20 = every 1 second, -1 disables it)
"permissionNameFormat": [ // Permission based overrides of name format
{
"permission": "some.permission", // Required permission
"opLevel": -1, // Alternative OP level (-1 to disable)
"style": "..." // format of player name (uses Text Parser and placeholders)
}
]
// Config version, do not change. Used only for updating from one version to another
"config_version": 2,
// Allows selecting id of default player list style
"default_style": "default",
// Allows changing messages sent by this mods commands.
"messages": {
"switch": "Your player list style has been changed to: <gold>${style}</gold>",
"unknown": "<red>This style doesn't exist!</red>",
"no_permission": "<red>You don't have required permissions!</red>"
},
// Modifies how player name is displayed
"player": {
// Toggles this feature.
"modify_name": false,
// Hides player name from player list. Doesn't have any effect on commands, suggestions or entity visibility!
"hidden": false,
// Disables this formatting, forcing it to use vanilla one.
"passthrough": false,
// Default format of player name
"format": "%player:displayname%",
// Enables sending updates when player sends a message
"update_on_chat_message": false,
// Enables sending updates every provided amount of ticks. -1 disables it
"update_tick_time": -1,
// Custom styles
"styles": [
{
// Requirement of style, used for applying
"require": {/* PREDICATE */},
// Applied formatting, same as one above
"format": "...",
// Optional. Disables this formatting, forcing it to use vanilla one.
"passthrough": false,
// Optional, hides player name from player list. Doesn't have any effect on commands, suggestions or entity visibility!
"hidden": false
}
]
},
// Makes player list show in singleplayer without lan enabled
"client_show_in_singleplayer": true
}
```
### Styles:
This mod allows having multiple styles, that can be selected by players (just put them in `./config/styledplayerlist/styles/` and use `/styledplayerlist reload` command)
[Formatting uses PlaceholderAPI's Text Parser for which docs you can find here](https://github.com/Patbox/FabricPlaceholderAPI/blob/1.17/TEXT_FORMATTING.md).

```json5
{
"id": "default", // used internally and for commands
"name": "Default", // used is messages
"header": [ // header of player list, every element is in new line
"",
"<gradient:#4adeff:#3d8eff><bold> Styled Player List</bold></gradient> ⛏ ",
"",
"<color:#555555><strikethrough> </strikethrough>[ </color><color:#FF5555>%server:online%<color:#6666676>/</color>%server:max_players%</color><color:#555555> ]<strikethrough> </strikethrough></color>",
""
],
"footer": [ // footer of player list, every element is in new line
"",
"<color:#555555><strikethrough> </strikethrough></color>",
"",
"<gray>TPS: %server:tps_colored% <dark_gray>|</dark_gray> <gray>Ping: <color:#ffba26>%player:ping%</color>",
""
// Predicate required for usage of this style, required by player
"require": {/* PREDICATE */},
// Style name used for display
"style_name": "Default",
// Time between updates of the style in ticks. 20 is 1 second. Used for formatting and placeholders
"update_tick_time": 20,
// Header of player list style, using simple/static definition (works in "list_footer" too). Allows formatting
"list_header": [
"...",
"..."
],
"hidden": false, // hides in commands
"permission": "" // required permission, leave empty if you want to allow everyone
// Footer of player list style, using animated definition (works in "list_header" too). Allows formatting
"list_footer": {
// Number of changes required to change into next frame. This means it updates every (change_rate * update_tick_time) ticks
"change_rate": 1,
// Frames of displayed text. There is no limit for amount of them
"values": [
[
"<red>..."
],
[
"<blue>..."
]
],
},
// Makes this style hidden from autocompletion, without changing requirements
"hidden_in_commands": false
}
```

Expand Down
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ dependencies {
modCompileOnly("net.fabricmc.fabric-api:fabric-api:${project.fabric_version}")
modLocalRuntime("net.fabricmc.fabric-api:fabric-api:${project.fabric_version}")

modImplementation include("eu.pb4:placeholder-api:2.0.0-pre.2+1.19.3")
modImplementation include("eu.pb4:placeholder-api:2.1.0+1.19.4")
modImplementation include("eu.pb4:player-data-api:0.2.2+1.19.3")
modImplementation include("me.lucko:fabric-permissions-api:0.1-SNAPSHOT")
modImplementation include("eu.pb4:predicate-api:0.1.1+1.19.4")

modCompileOnly "carpet:fabric-carpet:${project.carpet_core_version}"

Expand Down
10 changes: 5 additions & 5 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ org.gradle.jvmargs=-Xmx1G

# Fabric Properties
# check these on https://fabricmc.net/use
minecraft_version=1.19.3-rc1
yarn_mappings=1.19.3-rc1+build.1
loader_version=0.14.11
minecraft_version=1.19.4
yarn_mappings=1.19.4+build.1
loader_version=0.14.14

#Fabric api
fabric_version=0.68.1+1.19.3
fabric_version=0.75.1+1.19.4

# Mod Properties
mod_version = 2.3.0+1.19.3
mod_version = 3.0.0+1.19.4
maven_group = eu.pb4
archives_base_name = styledplayerlist

Expand Down
Binary file modified logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added logo.xcf
Binary file not shown.
Binary file added logo_512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions src/main/java/eu/pb4/styledplayerlist/CardboardWarning.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package eu.pb4.styledplayerlist;

import com.mojang.logging.LogUtils;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.entrypoint.PreLaunchEntrypoint;
import org.slf4j.Logger;

public class CardboardWarning implements PreLaunchEntrypoint {
public static final Logger LOGGER = LogUtils.getLogger();
public static final boolean LOADED = FabricLoader.getInstance().isModLoaded("cardboard") || FabricLoader.getInstance().isModLoaded("banner");

@Override
public void onPreLaunch() {
checkAndAnnounce();
}

public static void checkAndAnnounce() {
if (LOADED) {
LOGGER.error("==============================================");
for (var i = 0; i < 4; i++) {
LOGGER.error("");
LOGGER.error("Cardboard/Banner detected! This mod doesn't work with it!");
LOGGER.error("You won't get any support as long as it's present!");
LOGGER.error("");
LOGGER.error("Read more at: https://gist.github.com/Patbox/e44844294c358b614d347d369b0fc3bf");
LOGGER.error("");
LOGGER.error("==============================================");
}
}
}
}
160 changes: 160 additions & 0 deletions src/main/java/eu/pb4/styledplayerlist/GenericModInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package eu.pb4.styledplayerlist;

import net.fabricmc.loader.api.ModContainer;
import net.minecraft.text.*;
import net.minecraft.util.Formatting;

import javax.imageio.ImageIO;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class GenericModInfo {
private static final int COLOR = 0x3d8eff;

private static Text[] icon = new Text[0];
private static Text[] about = new Text[0];
private static Text[] consoleAbout = new Text[0];

public static void build(ModContainer container) {
var github = container.getMetadata().getContact().get("sources").orElse("UNKNOWN");
{
final String chr = "█";
var icon = new ArrayList<MutableText>();
try {
var source = ImageIO.read(Files.newInputStream(container.getPath("assets/styled_player_list/icon_ingame.png")));

for (int y = 0; y < source.getHeight(); y++) {
var base = Text.literal("");
int line = 0;
int color = source.getRGB(0, y) & 0xFFFFFF;
for (int x = 0; x < source.getWidth(); x++) {
int colorPixel = source.getRGB(x, y) & 0xFFFFFF;

if (color == colorPixel) {
line++;
} else {
base.append(Text.literal(chr.repeat(line)).setStyle(Style.EMPTY.withColor(color)));
color = colorPixel;
line = 1;
}
}

base.append(Text.literal(chr.repeat(line)).setStyle(Style.EMPTY.withColor(color)));
icon.add(base);
}
GenericModInfo.icon = icon.toArray(new Text[0]);
} catch (Throwable e) {
e.printStackTrace();
}

}

var contributors = new ArrayList<String>();

container.getMetadata().getAuthors().forEach(x -> contributors.add(x.getName()));
container.getMetadata().getContributors().forEach(x -> contributors.add(x.getName()));

var about = new ArrayList<Text>();
var extraData = Text.empty();
try {
extraData.append(Text.literal("[")
.append(Text.literal("Contributors")
.setStyle(Style.EMPTY.withColor(Formatting.AQUA)
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
Text.literal(String.join("\n", contributors))
))
))
.append("] ")
).append(Text.literal("[")
.append(Text.literal("Github")
.setStyle(Style.EMPTY.withColor(Formatting.BLUE).withUnderline(true)
.withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, github))
.withHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT,
Text.literal(github)
))
))
.append("]")).setStyle(Style.EMPTY.withColor(Formatting.DARK_GRAY));

about.add(Text.empty()
.append(Text.literal( container.getMetadata().getName() + " ").setStyle(Style.EMPTY.withColor(COLOR).withBold(true)))
.append(Text.literal(container.getMetadata().getVersion().getFriendlyString()).setStyle(Style.EMPTY.withColor(Formatting.WHITE))));

about.add(Text.literal("» " + container.getMetadata().getDescription()).setStyle(Style.EMPTY.withColor(Formatting.GRAY)));

about.add(extraData);
} catch (Throwable e) {
e.printStackTrace();
}

GenericModInfo.consoleAbout = about.toArray(new Text[0]);

if (icon.length == 0) {
GenericModInfo.about = GenericModInfo.consoleAbout;
} else {
var output = new ArrayList<Text>();
about.clear();
try {
about.add(Text.literal(container.getMetadata().getName()).setStyle(Style.EMPTY.withColor(COLOR).withBold(true).withClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, github))));
about.add(Text.literal("Version: ").setStyle(Style.EMPTY.withColor(0xf7e1a7))
.append(Text.literal(container.getMetadata().getVersion().getFriendlyString()).setStyle(Style.EMPTY.withColor(Formatting.WHITE))));

about.add(extraData);
about.add(Text.empty());

var desc = new ArrayList<>(List.of(container.getMetadata().getDescription().split(" ")));

if (desc.size() > 0) {
StringBuilder descPart = new StringBuilder();
while (!desc.isEmpty()) {
(descPart.isEmpty() ? descPart : descPart.append(" ")).append(desc.remove(0));

if (descPart.length() > 16) {
about.add(Text.literal(descPart.toString()).setStyle(Style.EMPTY.withColor(Formatting.GRAY)));
descPart = new StringBuilder();
}
}

if (descPart.length() > 0) {
about.add(Text.literal(descPart.toString()).setStyle(Style.EMPTY.withColor(Formatting.GRAY)));
}
}

if (icon.length > about.size() + 2) {
int a = 0;
for (int i = 0; i < icon.length; i++) {
if (i == (icon.length - about.size() - 1) / 2 + a && a < about.size()) {
output.add(icon[i].copy().append(" ").append(about.get(a++)));
} else {
output.add(icon[i]);
}
}
} else {
Collections.addAll(output, icon);
output.addAll(about);
}
} catch (Exception e) {
e.printStackTrace();
var invalid = Text.literal("/!\\ [ Invalid about mod info ] /!\\").setStyle(Style.EMPTY.withColor(0xFF0000).withItalic(true));

output.add(invalid);
about.add(invalid);
}

GenericModInfo.about = output.toArray(new Text[0]);
}
}

public static Text[] getIcon() {
return icon;
}

public static Text[] getAboutFull() {
return about;
}

public static Text[] getAboutConsole() {
return consoleAbout;
}
}
Loading

0 comments on commit 8329e8b

Please sign in to comment.