Skip to content

Commit 54a7622

Browse files
wizjanydordsor21
authored andcommitted
Add text3 bukkit adapter override for Spigot gson change. (#2759)
1 parent ce4d616 commit 54a7622

File tree

2 files changed

+205
-0
lines changed

2 files changed

+205
-0
lines changed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* This file is part of text-extras, licensed under the MIT License.
3+
*
4+
* Copyright (c) 2018-2020 KyoriPowered
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
package com.sk89q.worldedit.util.formatting.text.adapter.bukkit;
26+
27+
import com.google.common.collect.Lists;
28+
import com.google.gson.Gson;
29+
import com.google.gson.GsonBuilder;
30+
import com.google.gson.JsonElement;
31+
import com.google.gson.JsonSerializationContext;
32+
import com.google.gson.JsonSerializer;
33+
import com.google.gson.TypeAdapterFactory;
34+
import com.google.gson.internal.Excluder;
35+
import com.google.gson.reflect.TypeToken;
36+
import com.sk89q.worldedit.util.formatting.text.Component;
37+
import com.sk89q.worldedit.util.formatting.text.serializer.gson.GsonComponentSerializer;
38+
import com.sk89q.worldedit.util.formatting.text.serializer.legacy.LegacyComponentSerializer;
39+
import net.md_5.bungee.api.ChatMessageType;
40+
import net.md_5.bungee.api.chat.BaseComponent;
41+
import net.md_5.bungee.chat.ChatVersion;
42+
import net.md_5.bungee.chat.ComponentSerializer;
43+
import net.md_5.bungee.chat.VersionedComponentSerializer;
44+
import org.bukkit.command.CommandSender;
45+
import org.bukkit.entity.Player;
46+
47+
import java.lang.reflect.Field;
48+
import java.lang.reflect.Method;
49+
import java.lang.reflect.Type;
50+
import java.util.ArrayList;
51+
import java.util.Collections;
52+
import java.util.Iterator;
53+
import java.util.List;
54+
import java.util.function.BiConsumer;
55+
56+
57+
@SuppressWarnings({"deprecation", "UnstableApiUsage", "unchecked"})
58+
final class SpigotAdapter implements Adapter {
59+
private static final boolean BOUND = bind();
60+
61+
private static boolean bind() {
62+
try {
63+
final Field factoriesField = field(Gson.class, "factories");
64+
final Field builderFactoriesField = field(GsonBuilder.class, "factories");
65+
final Field builderHierarchyFactoriesField = field(GsonBuilder.class, "hierarchyFactories");
66+
67+
// WorldEdit start - use different gson depending on MC version
68+
final Gson gson;
69+
Gson tmpGson;
70+
try {
71+
final Field gsonField = field(ComponentSerializer.class, "gson");
72+
tmpGson = (Gson) gsonField.get(null);
73+
} catch (NoSuchFieldException ignored) {
74+
tmpGson = VersionedComponentSerializer.forVersion(ChatVersion.V1_21_5).getGson();
75+
}
76+
gson = tmpGson;
77+
// WorldEdit end
78+
79+
final GsonBuilder builder = GsonComponentSerializer.populate(new GsonBuilder());
80+
81+
final List<TypeAdapterFactory> existingFactories = (List<TypeAdapterFactory>) factoriesField.get(gson);
82+
final List<TypeAdapterFactory> newFactories = new ArrayList<>();
83+
newFactories.addAll((List<TypeAdapterFactory>) builderFactoriesField.get(builder));
84+
Collections.reverse(newFactories);
85+
newFactories.addAll((List<TypeAdapterFactory>) builderHierarchyFactoriesField.get(builder));
86+
87+
final List<TypeAdapterFactory> modifiedFactories = new ArrayList<>(existingFactories);
88+
89+
// the excluder must precede all adapters that handle user-defined types
90+
final int index = findExcluderIndex(modifiedFactories);
91+
92+
for (final TypeAdapterFactory newFactory : Lists.reverse(newFactories)) {
93+
modifiedFactories.add(index, newFactory);
94+
}
95+
96+
Class<?> treeTypeAdapterClass;
97+
try {
98+
// newer gson releases
99+
treeTypeAdapterClass = Class.forName("com.google.gson.internal.bind.TreeTypeAdapter");
100+
} catch (final ClassNotFoundException e) {
101+
// old gson releases
102+
treeTypeAdapterClass = Class.forName("com.google.gson.TreeTypeAdapter");
103+
}
104+
105+
final Method newFactoryWithMatchRawTypeMethod = treeTypeAdapterClass.getMethod("newFactoryWithMatchRawType", TypeToken.class, Object.class);
106+
final TypeAdapterFactory adapterComponentFactory = (TypeAdapterFactory) newFactoryWithMatchRawTypeMethod.invoke(null, TypeToken.get(AdapterComponent.class), new Serializer());
107+
modifiedFactories.add(index, adapterComponentFactory);
108+
109+
factoriesField.set(gson, modifiedFactories);
110+
return true;
111+
} catch (final Throwable e) {
112+
return false;
113+
}
114+
}
115+
116+
private static Field field(final Class<?> klass, final String name) throws NoSuchFieldException {
117+
final Field field = klass.getDeclaredField(name);
118+
field.setAccessible(true);
119+
return field;
120+
}
121+
122+
private static int findExcluderIndex(final List<TypeAdapterFactory> factories) {
123+
for (int i = 0, size = factories.size(); i < size; i++) {
124+
final TypeAdapterFactory factory = factories.get(i);
125+
if (factory instanceof Excluder) {
126+
return i + 1;
127+
}
128+
}
129+
return 0;
130+
}
131+
132+
@Override
133+
public void sendMessage(final List<? extends CommandSender> viewers, final Component component) {
134+
if (!BOUND) {
135+
return;
136+
}
137+
send(viewers, component, (viewer, components) -> viewer.spigot().sendMessage(components));
138+
}
139+
140+
@Override
141+
public void sendActionBar(final List<? extends CommandSender> viewers, final Component component) {
142+
if (!BOUND) {
143+
return;
144+
}
145+
send(viewers, component, (viewer, components) -> viewer.spigot().sendMessage(ChatMessageType.ACTION_BAR, components));
146+
}
147+
148+
private static void send(final List<? extends CommandSender> viewers, final Component component, final BiConsumer<Player, BaseComponent[]> consumer) {
149+
if (!BOUND) {
150+
return;
151+
}
152+
final BaseComponent[] components = {new AdapterComponent(component)};
153+
for (final Iterator<? extends CommandSender> it = viewers.iterator(); it.hasNext(); ) {
154+
final CommandSender viewer = it.next();
155+
if (viewer instanceof Player) {
156+
try {
157+
consumer.accept((Player) viewer, components);
158+
it.remove();
159+
} catch (final Throwable e) {
160+
e.printStackTrace();
161+
}
162+
}
163+
}
164+
}
165+
166+
static BaseComponent[] toBungeeCord(final Component component) {
167+
if (BOUND) {
168+
return new BaseComponent[]{new AdapterComponent(component)};
169+
} else {
170+
return ComponentSerializer.parse(GsonComponentSerializer.INSTANCE.serialize(component));
171+
}
172+
}
173+
174+
public static final class AdapterComponent extends BaseComponent {
175+
private final Component component;
176+
177+
@SuppressWarnings("deprecation")
178+
// weeeee
179+
AdapterComponent(final Component component) {
180+
this.component = component;
181+
}
182+
183+
@Override
184+
public BaseComponent duplicate() {
185+
return this;
186+
}
187+
188+
@Override
189+
public String toLegacyText() {
190+
return LegacyComponentSerializer.legacy().serialize(this.component);
191+
}
192+
}
193+
194+
public static class Serializer implements JsonSerializer<AdapterComponent> {
195+
@Override
196+
public JsonElement serialize(final AdapterComponent src, final Type typeOfSrc, final JsonSerializationContext context) {
197+
return context.serialize(src.component);
198+
}
199+
}
200+
}

worldedit-bukkit/src/test/java/com/sk89q/wepif/TestOfflinePermissible.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,4 +376,9 @@ public void setStatistic(
376376
return null;
377377
}
378378

379+
@Override
380+
public PersistentDataContainerView getPersistentDataContainer() {
381+
return null;
382+
}
383+
379384
}

0 commit comments

Comments
 (0)