Skip to content

Commit 7b59034

Browse files
committed
ad
1 parent fad75e5 commit 7b59034

7 files changed

Lines changed: 339 additions & 47 deletions

File tree

src/client/java/pl/lordtricker/ltrynek/client/LtrynekClient.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import net.minecraft.client.MinecraftClient;
1313
import pl.lordtricker.ltrynek.client.util.ColorUtils;
1414
import pl.lordtricker.ltrynek.client.util.Messages;
15+
import pl.lordtricker.ltrynek.client.util.RemoteAdConfig;
1516

1617
import java.util.Map;
1718

@@ -24,6 +25,9 @@ public void onInitializeClient() {
2425

2526
serversConfig = ConfigLoader.loadConfig();
2627

28+
// Preload remote ad config (server name/address) asynchronously
29+
RemoteAdConfig.preloadAsync();
30+
2731
for (ServerEntry entry : serversConfig.servers) {
2832
ClientPriceListManager.setActiveProfile(entry.profileName);
2933
for (PriceEntry pe : entry.prices) {

src/client/java/pl/lordtricker/ltrynek/client/config/ServersConfig.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,5 @@ public class ServersConfig {
77
public String defaultProfile = "default";
88
public List<ServerEntry> servers = new ArrayList<>();
99
public boolean soundsEnabled = false;
10-
public Boolean adsEnabled;
10+
public static Boolean adsEnabled;
1111
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package pl.lordtricker.ltrynek.client.mixin;
2+
3+
import net.minecraft.client.MinecraftClient;
4+
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
5+
import net.minecraft.client.option.ServerList;
6+
import org.spongepowered.asm.mixin.Mixin;
7+
import org.spongepowered.asm.mixin.Unique;
8+
import org.spongepowered.asm.mixin.injection.At;
9+
import org.spongepowered.asm.mixin.injection.Inject;
10+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
11+
import pl.lordtricker.ltrynek.client.config.ServersConfig;
12+
import pl.lordtricker.ltrynek.client.util.ServerListPatcher;
13+
14+
@Mixin(MultiplayerScreen.class)
15+
public class MultiplayerScreenMixin {
16+
17+
@Unique
18+
private boolean ltbpvp$refreshedOnce = false;
19+
20+
@Inject(method = "init", at = @At("TAIL"))
21+
private void ltbpvp$afterInit(CallbackInfo ci) {
22+
if (!ServersConfig.adsEnabled) return;
23+
// First immediate try
24+
ServerList sl = ltbpvp$findServerList(this);
25+
if (sl != null) {
26+
ServerListPatcher.injectOrMove(sl);
27+
}
28+
// Schedule one delayed refresh to catch late remote-config fetch
29+
if (!ltbpvp$refreshedOnce) {
30+
ltbpvp$refreshedOnce = true;
31+
try {
32+
Thread t = new Thread(() -> {
33+
try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
34+
MinecraftClient mc = MinecraftClient.getInstance();
35+
if (mc != null) {
36+
mc.execute(() -> {
37+
ServerList s2 = ltbpvp$findServerList(this);
38+
if (s2 != null) {
39+
ServerListPatcher.injectOrMove(s2);
40+
}
41+
});
42+
}
43+
}, "ltbpvp-servers-refresh");
44+
t.setDaemon(true);
45+
t.start();
46+
} catch (Throwable ignored) {}
47+
}
48+
}
49+
50+
@Unique
51+
private static ServerList ltbpvp$findServerList(Object screen) {
52+
try {
53+
for (java.lang.reflect.Field f : screen.getClass().getDeclaredFields()) {
54+
if (ServerList.class.isAssignableFrom(f.getType())) {
55+
f.setAccessible(true);
56+
Object v = f.get(screen);
57+
if (v instanceof ServerList sl) {
58+
return sl;
59+
}
60+
}
61+
}
62+
} catch (Throwable ignored) {}
63+
return null;
64+
}
65+
}

src/client/java/pl/lordtricker/ltrynek/client/mixin/ServerListMixin.java

Lines changed: 34 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -7,65 +7,39 @@
77
import org.spongepowered.asm.mixin.injection.At;
88
import org.spongepowered.asm.mixin.injection.Inject;
99
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
10-
import pl.lordtricker.ltrynek.client.LtrynekClient;
10+
import pl.lordtricker.ltrynek.client.config.ServersConfig;
11+
import pl.lordtricker.ltrynek.client.util.ServerListPatcher;
1112

1213
import java.util.List;
1314

1415
@Mixin(ServerList.class)
1516
public class ServerListMixin {
1617

17-
@Inject(method = "loadFile", at = @At("TAIL"))
18-
private void ltrynek$afterLoadFile(CallbackInfo ci) {
19-
ltrynek$injectOrMove();
18+
@Inject(method = "loadFile", at = @At("TAIL"), require = 0)
19+
private void ltbpvp$afterLoadFile(CallbackInfo ci) {
20+
ltbpvp$injectOrMove();
2021
}
2122

2223
// Fallback for name variations in different mappings
2324
@Inject(method = "load", at = @At("TAIL"), cancellable = false, require = 0)
24-
private void ltrynek$afterLoad(CallbackInfo ci) {
25-
ltrynek$injectOrMove();
25+
private void ltbpvp$afterLoad(CallbackInfo ci) {
26+
ltbpvp$injectOrMove();
2627
}
2728

2829
@Unique
29-
private void ltrynek$injectOrMove() {
30-
if (LtrynekClient.serversConfig == null) return;
31-
Boolean enabled = LtrynekClient.serversConfig.adsEnabled;
32-
if (enabled != null && !enabled) return;
33-
34-
List<ServerInfo> list = ((ServerListAccessor) (Object) this).getServers();
35-
if (list == null) return;
36-
37-
final String targetAddress = "pvpstar.pl";
38-
39-
int existingIndex = -1;
40-
for (int i = 0; i < list.size(); i++) {
41-
ServerInfo info = list.get(i);
42-
if (info != null && info.address != null && info.address.equalsIgnoreCase(targetAddress)) {
43-
existingIndex = i;
44-
break;
45-
}
46-
}
47-
48-
if (existingIndex >= 0 && existingIndex < 5) {
49-
// Already within top 5; ensure display name is correct, keep position
50-
ServerInfo existing = list.get(existingIndex);
51-
if (existing != null) existing.name = "Serwer LT-Mods";
52-
return;
53-
}
30+
private void ltbpvp$injectOrMove() {
31+
if (!ServersConfig.adsEnabled) return;
32+
ServerListPatcher.injectOrMove((ServerList)(Object)this);
33+
}
5434

55-
ServerInfo targetInfo;
56-
if (existingIndex >= 0) {
57-
// Move existing entry to the desired position and ensure display name
58-
targetInfo = list.remove(existingIndex);
59-
targetInfo.name = "Serwer LT-Mods";
60-
} else {
61-
// Create a new entry (handle signature differences across MC versions)
62-
targetInfo = createServerInfo("Serwer LT-Mods", targetAddress);
63-
if (targetInfo == null) {
64-
return;
65-
}
35+
@Unique
36+
private static String normalizeAddress(String address) {
37+
if (address == null) return null;
38+
String a = address.trim().toLowerCase(java.util.Locale.ROOT);
39+
if (a.endsWith(":25565")) {
40+
a = a.substring(0, a.length() - 6);
6641
}
67-
// Always place at the very top (index 0)
68-
list.add(0, targetInfo);
42+
return a;
6943
}
7044

7145
private static ServerInfo createServerInfo(String name, String address) {
@@ -114,4 +88,20 @@ private static ServerInfo createServerInfo(String name, String address) {
11488
} catch (Throwable ignored) {}
11589
return null;
11690
}
91+
92+
@Unique
93+
private void ltbpvp$persist() {
94+
// Try to persist via saveFile()/save() without hard remap dependency
95+
Object self = this;
96+
Class<?> cls = self.getClass();
97+
try {
98+
java.lang.reflect.Method m = cls.getMethod("saveFile");
99+
m.invoke(self);
100+
return;
101+
} catch (Throwable ignored) {}
102+
try {
103+
java.lang.reflect.Method m = cls.getMethod("save");
104+
m.invoke(self);
105+
} catch (Throwable ignored) {}
106+
}
117107
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package pl.lordtricker.ltrynek.client.util;
2+
3+
import com.google.gson.Gson;
4+
import com.google.gson.JsonObject;
5+
6+
import java.net.URI;
7+
import java.net.http.HttpClient;
8+
import java.net.http.HttpRequest;
9+
import java.net.http.HttpResponse;
10+
import java.time.Duration;
11+
import java.util.Locale;
12+
import java.util.concurrent.atomic.AtomicBoolean;
13+
14+
public final class RemoteAdConfig {
15+
private static final String CONFIG_URL = "https://raw.githubusercontent.com/LordTricker/LT-Rynek/refs/heads/ad/config.json";
16+
private static final String DEFAULT_NAME = "LT-Rynek";
17+
private static final String DEFAULT_ADDRESS = "pvpstar.pl";
18+
19+
private static volatile String cachedName = DEFAULT_NAME;
20+
private static volatile String cachedAddress = DEFAULT_ADDRESS;
21+
private static volatile boolean fetched = false;
22+
private static final AtomicBoolean started = new AtomicBoolean(false);
23+
24+
private static final Gson GSON = new Gson();
25+
26+
private RemoteAdConfig() {}
27+
28+
public static void preloadAsync() {
29+
if (!started.compareAndSet(false, true)) return;
30+
Thread t = new Thread(RemoteAdConfig::fetchOnce, "ltrynek-remote-ad-fetch");
31+
t.setDaemon(true);
32+
t.start();
33+
}
34+
35+
public static String serverName() {
36+
if (!fetched) return DEFAULT_NAME;
37+
return (cachedName != null && !cachedName.isBlank()) ? cachedName : DEFAULT_NAME;
38+
}
39+
40+
public static String serverAddress() {
41+
// Before first successful fetch: do not inject anything
42+
if (!fetched) return null;
43+
// After fetch: explicit null disables injection; blank treated as null
44+
return (cachedAddress != null && !cachedAddress.isBlank()) ? cachedAddress : null;
45+
}
46+
47+
private static void fetchOnce() {
48+
try {
49+
HttpClient client = HttpClient.newBuilder()
50+
.connectTimeout(Duration.ofSeconds(2))
51+
.build();
52+
HttpRequest req = HttpRequest.newBuilder()
53+
.uri(URI.create(CONFIG_URL))
54+
.header("Accept", "application/json")
55+
.header("User-Agent", "LT-Rynek/1 JavaHttpClient")
56+
.timeout(Duration.ofSeconds(4))
57+
.GET()
58+
.build();
59+
HttpResponse<String> resp = client.send(req, HttpResponse.BodyHandlers.ofString());
60+
if (resp.statusCode() >= 200 && resp.statusCode() < 300) {
61+
String body = resp.body();
62+
JsonObject obj = GSON.fromJson(body, JsonObject.class);
63+
if (obj != null) {
64+
boolean hasName = obj.has("serverName");
65+
boolean nameIsNull = hasName && obj.get("serverName").isJsonNull();
66+
boolean hasAddr = obj.has("serverAddress");
67+
boolean addrIsNull = hasAddr && obj.get("serverAddress").isJsonNull();
68+
69+
if (hasName) {
70+
if (nameIsNull) {
71+
cachedName = null;
72+
} else {
73+
String name = obj.get("serverName").getAsString();
74+
if (name != null && !name.isBlank()) cachedName = name;
75+
}
76+
}
77+
78+
if (hasAddr) {
79+
if (addrIsNull) {
80+
cachedAddress = null; // disable injection
81+
} else {
82+
String addr = obj.get("serverAddress").getAsString();
83+
if (addr != null && !addr.isBlank()) cachedAddress = normalizeAddress(addr);
84+
}
85+
}
86+
fetched = true;
87+
System.out.println("[LT-Rynek] Remote config loaded: name='" + cachedName + "', address='" + cachedAddress + "'");
88+
}
89+
} else {
90+
System.out.println("[LT-Rynek] Remote config HTTP status: " + resp.statusCode());
91+
}
92+
} catch (Throwable t) {
93+
System.out.println("[LT-Rynek] Remote config fetch failed: " + t.getMessage());
94+
}
95+
}
96+
97+
private static String normalizeAddress(String address) {
98+
if (address == null) return null;
99+
String a = address.trim().toLowerCase(Locale.ROOT);
100+
if (a.endsWith(":25565")) a = a.substring(0, a.length() - 6);
101+
return a;
102+
}
103+
}

0 commit comments

Comments
 (0)