Copyright (C)
3 | * Kristian S. Strangeland
4 | *
5 | * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
6 | * License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later
7 | * version.
8 | *
9 | * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
10 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
11 | * details.
12 | *
13 | * You should have received a copy of the GNU General Public License along with this program. If not, see
14 | * .
15 | */
16 | package me.tom.sparse.spigot.chat.protocol;
17 |
18 | import com.comphenix.protocol.PacketType;
19 | import com.comphenix.protocol.events.PacketContainer;
20 | import com.comphenix.protocol.wrappers.EnumWrappers;
21 | import com.comphenix.protocol.wrappers.EnumWrappers.ChatType;
22 | import com.comphenix.protocol.wrappers.WrappedChatComponent;
23 |
24 | import java.util.Arrays;
25 |
26 | public class WrapperPlayServerChat extends AbstractPacket {
27 | public static final PacketType TYPE = PacketType.Play.Server.CHAT;
28 |
29 | public WrapperPlayServerChat() {
30 | super(new PacketContainer(TYPE), TYPE);
31 | handle.getModifier().writeDefaults();
32 | }
33 |
34 | public WrapperPlayServerChat(PacketContainer packet) {
35 | super(packet, TYPE);
36 | }
37 |
38 | /**
39 | * Retrieve the chat message.
40 | *
41 | * Limited to 32767 bytes
42 | *
43 | * @return The current message
44 | */
45 | public WrappedChatComponent getMessage() {
46 | return handle.getChatComponents().read(0);
47 | }
48 |
49 | /**
50 | * Set the message.
51 | *
52 | * @param value - new value.
53 | */
54 | public void setMessage(WrappedChatComponent value) {
55 | handle.getChatComponents().write(0, value);
56 | }
57 |
58 | public ChatType getChatType() {
59 | return handle.getChatTypes().read(0);
60 | }
61 |
62 | public void setChatType(ChatType type) {
63 | handle.getChatTypes().write(0, type);
64 | }
65 |
66 | /**
67 | * Retrieve Position.
68 | *
69 | * Notes: 0 - Chat (chat box) ,1 - System Message (chat box), 2 - Above action bar
70 | *
71 | * @return The current Position
72 | * @deprecated Magic values replaced by enum
73 | */
74 | @Deprecated
75 | public byte getPosition() {
76 | Byte position = handle.getBytes().readSafely(0);
77 | if (position != null) {
78 | return position;
79 | } else {
80 | return getChatType().getId();
81 | }
82 | }
83 |
84 | /**
85 | * Set Position.
86 | *
87 | * @param value - new value.
88 | * @deprecated Magic values replaced by enum
89 | */
90 | @Deprecated
91 | public void setPosition(byte value) {
92 | handle.getBytes().writeSafely(0, value);
93 |
94 | if (EnumWrappers.getChatTypeClass() != null) {
95 | Arrays.stream(ChatType.values()).filter(t -> t.getId() == value).findAny()
96 | .ifPresent(t -> handle.getChatTypes().writeSafely(0, t));
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/me/tom/sparse/spigot/chat/util/NumberFormat.java:
--------------------------------------------------------------------------------
1 | package me.tom.sparse.spigot.chat.util;
2 |
3 | public interface NumberFormat {
4 | NumberFormat NONE = (v, l) -> "";
5 | NumberFormat FRACTION = (v, l) -> String.format("%d/%d", v + 1, l);
6 | NumberFormat PERCENTAGE = (v, l) -> String.format("%.1f%%", ((double) (v + 1) / l) * 100);
7 |
8 | String format(int value, int length);
9 | }
10 |
--------------------------------------------------------------------------------
/src/main/java/me/tom/sparse/spigot/chat/util/State.java:
--------------------------------------------------------------------------------
1 | package me.tom.sparse.spigot.chat.util;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | import java.util.Objects;
7 | import java.util.Optional;
8 | import java.util.function.Consumer;
9 | import java.util.function.Function;
10 |
11 | public class State {
12 | private Consumer> changeCallback;
13 |
14 | @NotNull
15 | private Function valueFilter;
16 |
17 | @Nullable
18 | private V current;
19 | @Nullable
20 | private V previous;
21 |
22 | /**
23 | * Constructs a new {@code State} with the provided value and the provided value filter.
24 | *
25 | * The value filter will replace the value every time {@link State#setCurrent} is called.
26 | *
27 | * @param current the starting value
28 | * @param valueFilter the filter for every value
29 | */
30 | public State(@Nullable V current, @Nullable Function valueFilter) {
31 | this.valueFilter = valueFilter == null ? v -> v : valueFilter;
32 | this.current = this.valueFilter.apply(current);
33 | }
34 |
35 | /**
36 | * Constructs a new {@code State} with the provided value and no input filter.
37 | *
38 | * @param current the starting value
39 | */
40 | public State(@Nullable V current) {
41 | this(current, v -> v);
42 | }
43 |
44 | /**
45 | * Sets the current value if the provided value is not {@link Object#equals} to the old one, then calls the {@code
46 | * changeCallback}.
47 | *
48 | * @param newValue the new value
49 | */
50 | public void setCurrent(@Nullable V newValue) {
51 | newValue = valueFilter.apply(newValue);
52 |
53 | if (Objects.equals(newValue, this.current))
54 | return;
55 |
56 | this.previous = this.current;
57 | this.current = newValue;
58 |
59 | if (changeCallback != null)
60 | changeCallback.accept(this);
61 | }
62 |
63 | /**
64 | * @return the current value as an {@link java.util.Optional}
65 | */
66 | public Optional getOptionalCurrent() {
67 | return Optional.ofNullable(current);
68 | }
69 |
70 | /**
71 | * @return the previous value as an {@link java.util.Optional}
72 | */
73 | public Optional getOptionalPrevious() {
74 | return Optional.ofNullable(previous);
75 | }
76 |
77 | /**
78 | * @return the current value. Might be {@code null}.
79 | */
80 | @Nullable
81 | public V getCurrent() {
82 | return current;
83 | }
84 |
85 | /**
86 | * @return the getPrevious value. Might be {@code null}.
87 | */
88 | @Nullable
89 | public V getPrevious() {
90 | return previous;
91 | }
92 |
93 | /**
94 | * Sets the change callback. Every time this {@code State} changes, the provided callback will be called.
95 | *
96 | * Replaces any previously setCurrent change callbacks.
97 | *
98 | * @param changeCallback the new change callback.
99 | */
100 | public void setChangeCallback(@NotNull Consumer> changeCallback) {
101 | this.changeCallback = changeCallback;
102 | }
103 |
104 | public boolean equals(Object o) {
105 | if (this == o) return true;
106 | if (!(o instanceof State)) return false;
107 |
108 | State> state = (State>) o;
109 |
110 | return current != null ? current.equals(state.current) : state.current == null;
111 | }
112 |
113 | public int hashCode() {
114 | return current != null ? current.hashCode() : 0;
115 | }
116 |
117 | public String toString() {
118 | return "State{" +
119 | "current=" + current +
120 | ", previous=" + previous +
121 | '}';
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/src/main/java/me/tom/sparse/spigot/chat/util/Text.java:
--------------------------------------------------------------------------------
1 | package me.tom.sparse.spigot.chat.util;
2 |
3 | import net.md_5.bungee.api.chat.BaseComponent;
4 | import net.md_5.bungee.api.chat.TextComponent;
5 |
6 | import org.jetbrains.annotations.NotNull;
7 |
8 | import java.util.ArrayList;
9 | import java.util.Arrays;
10 | import java.util.Collection;
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | import me.tom.sparse.spigot.chat.menu.ChatMenuAPI;
15 |
16 | /**
17 | * BaseComponent[] wrapper with cached width
18 | */
19 | public class Text {
20 | @NotNull
21 | protected List components = new ArrayList<>();
22 | protected int width = 0;
23 |
24 | /**
25 | * Constructs an empty {@code Text} object with 0 width and no components.
26 | */
27 | public Text() {
28 |
29 | }
30 |
31 | /**
32 | * ] Constructs a {@code Text} object with the provided text.
33 | *
34 | * @param text the starting text
35 | */
36 | public Text(@NotNull String text) {
37 | if (text.contains("\n"))
38 | throw new IllegalArgumentException("Text cannot have newline characters");
39 | Collections.addAll(components, TextComponent.fromLegacyText(text));
40 |
41 | calculateWidth();
42 | }
43 |
44 | /**
45 | * Constructs a {@code Text} object with the provided components
46 | *
47 | * @param components the starting components
48 | */
49 | public Text(@NotNull BaseComponent... components) {
50 | this(Arrays.asList(components));
51 | }
52 |
53 | /**
54 | * Constructs a {@code Text} object with the provided components
55 | *
56 | * @param components the starting components
57 | */
58 | public Text(@NotNull Collection components) {
59 | this.components.addAll(components);
60 | if (toLegacyText().contains("\n"))
61 | throw new IllegalArgumentException("Text cannot have newline characters");
62 | calculateWidth();
63 | }
64 |
65 | /**
66 | * @return the cached width
67 | */
68 | public int getWidth() {
69 | return width;
70 | }
71 |
72 | /**
73 | * Appends all of the components of the provided {@code Text} object to this.
74 | *
75 | * @param other the {@code Text} to append
76 | */
77 | public void append(@NotNull Text other) {
78 | components.addAll(other.components);
79 | width += other.width;
80 | }
81 |
82 | /**
83 | * Converts the provided text from legacy text to components and appends it
84 | *
85 | * @param text the text to append
86 | */
87 | public void append(@NotNull String text) {
88 | if (text.contains("\n"))
89 | throw new IllegalArgumentException("Text cannot have newline characters");
90 | Collections.addAll(components, TextComponent.fromLegacyText(text));
91 | calculateWidth();
92 | }
93 |
94 | /**
95 | * Appends all of the provided components
96 | *
97 | * @param components the components to append
98 | */
99 | public void append(@NotNull BaseComponent... components) {
100 | Collections.addAll(this.components, components);
101 | calculateWidth();
102 | }
103 |
104 | /**
105 | * Appends spaces to the end such that the width is as close as possible to the target width
106 | *
107 | * The resulting width may be more or less than the target width by 1-2 pixels
108 | *
109 | * @param targetWidth the width to expand to
110 | */
111 | public void expandToWidth(int targetWidth) {
112 | calculateWidth();
113 |
114 | if (width >= targetWidth)
115 | return;
116 |
117 | components.add(new TextComponent(TextUtil.generateSpaces((int) Math.round((targetWidth - width) / 4.0))));
118 | }
119 |
120 | /**
121 | * Appends spaces to the end such that the width is as close as possible to the target width without going over.
122 | *
123 | * The resulting width may be less than the target width 1-3 pixels
124 | *
125 | * @param targetWidth the width to expand to
126 | */
127 | public void expandToWidthNoExceed(int targetWidth) {
128 | calculateWidth();
129 |
130 | if (width >= targetWidth)
131 | return;
132 |
133 | components.add(new TextComponent(TextUtil.generateSpaces((int) Math.floor((targetWidth - width) / 4.0))));
134 | }
135 |
136 | /**
137 | * Recalculates the width of all the components
138 | */
139 | public void calculateWidth() {
140 | width = ChatMenuAPI.getWidth(toLegacyText());
141 | }
142 |
143 | /**
144 | * @return the components of this {@code Text} object converted to legacy.
145 | */
146 | @NotNull
147 | public String toLegacyText() {
148 | return TextComponent.toLegacyText(components.toArray(new BaseComponent[components.size()]));
149 | }
150 |
151 | /**
152 | * If you make any changes to this list, call {@link Text#calculateWidth()}
153 | *
154 | * @return the backing list of the components in this {@code Text} object.
155 | */
156 | @NotNull
157 | public List getComponents() {
158 | return components;
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/src/main/java/me/tom/sparse/spigot/chat/util/TextUtil.java:
--------------------------------------------------------------------------------
1 | package me.tom.sparse.spigot.chat.util;
2 |
3 | import java.util.Arrays;
4 |
5 | import me.tom.sparse.spigot.chat.menu.ChatMenuAPI;
6 |
7 | public final class TextUtil {
8 | public static String generateSpaces(int count) {
9 | return repeatCharacter(' ', count);
10 | }
11 |
12 | public static String repeatCharacter(char character, int count) {
13 | char[] chars = new char[count];
14 | Arrays.fill(chars, character);
15 | return new String(chars);
16 | }
17 |
18 | public static String generateWidth(char character, int width, boolean canExceed) {
19 | int charWidth = ChatMenuAPI.getCharacterWidth(character);
20 | int count = (int) (canExceed ? Math.round(width / (double) charWidth) : Math.floor(width / (double) charWidth));
21 | return repeatCharacter(character, count);
22 | }
23 |
24 | private TextUtil() {
25 | }
26 | }
27 |
--------------------------------------------------------------------------------