2
0
mirror of https://github.com/proxiodev/RedisBungee.git synced 2026-04-08 16:10:26 +00:00

implement the ProxyManager

untested
This commit is contained in:
2026-03-28 19:31:31 +04:00
parent 5e4b151d44
commit b855764e19
11 changed files with 311 additions and 2 deletions

View File

@@ -1,6 +1,7 @@
plugins {
alias(libs.plugins.blossom)
alias(libs.plugins.indragit)
alias(libs.plugins.protobuf)
}
description = "Core functions for valiobungee"
@@ -20,3 +21,24 @@ java {
withJavadocJar()
withSourcesJar()
}
dependencies {
api(project(":valiobungee-api"))
api(libs.protobuf)
api(libs.caffeine)
api(libs.slf4j)
testImplementation(libs.testing.juipter)
testImplementation(libs.testing.slf4j.simple)
}
tasks.test {
useJUnitPlatform()
}
protobuf {
protoc {
artifact = libs.protoc.get().toString()
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright (c) 2026-present ValioBungee contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU GENERAL PUBLIC LICENSE Version 3
* which accompanies this distribution, and is available at
* https://www.gnu.org/licenses/gpl-3.0.txt
*/
package net.limework.valiobungee.core;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.RemovalCause;
import com.github.benmanes.caffeine.cache.RemovalListener;
import java.time.Duration;
import java.util.UUID;
import net.limework.valiobungee.core.proto.messages.*;
import net.limework.valiobungee.core.util.logging.LogProviderFactory;
import org.slf4j.Logger;
/**
* This abstract class is responsible for proxy discovery and count online players as its cached
* locally Why abstract? Because it allows us to develop alternative implementation to use other
* software than valkey or redis
*/
public abstract class ProxyManager {
protected final UUID proxyManagerId = UUID.randomUUID();
protected final ValioBungeePlatform platform;
public ProxyManager(ValioBungeePlatform platform) {
this.platform = platform;
}
protected final Logger log = LogProviderFactory.get();
// proxy info class here stores the ProxyManager ID hence we use another cache for online players
// reason we use caffeine cache it allows us to auto remove entries without need for scheduled
// tasks in the proxies when a proxy disappears without sending death message
private final Cache<String, Heartbeat> heartbeats =
Caffeine.newBuilder()
.expireAfterWrite(Duration.ofSeconds(30))
.removalListener(
(RemovalListener<String, Heartbeat>)
(key, value, cause) -> {
if (cause == RemovalCause.EXPIRED) {
assert value != null;
log.warn("proxy {} has disconnected but did not send death message", value);
}
})
.build();
protected void handleProxyHeartBeat(Heartbeat payload) {
if (!this.heartbeats.asMap().containsKey(payload.getSender().getProxyId()))
log.info("Proxy {} has connected!", payload.getSender().getProxyId());
this.heartbeats.put(payload.getSender().getProxyId(), payload);
}
protected void handleProxyDeath(Death payload) {
this.heartbeats.invalidate(payload.getSender().getProxyId());
log.info("Proxy {} has disconnected", payload.getSender().getProxyId());
}
public int onlinePlayersCount() {
// reason we + local online players because our proxy is not inside it own heartbeat cache
return heartbeats.asMap().values().stream().mapToInt(Heartbeat::getOnlinePlayersCount).sum()
+ platform.localOnlinePlayers();
}
public int onlinePlayersCount(String proxyId) {
if (proxyId.equals(this.platform.proxyId())) {
return platform.localOnlinePlayers();
} else {
if (!heartbeats.asMap().containsKey(proxyId)) return 0; // don't error?
return heartbeats.asMap().get(proxyId).getOnlinePlayersCount();
}
}
protected ProxyInfo createProxyInfo() {
return ProxyInfo.newBuilder()
.setProxyId(platform.proxyId())
.setProxyManagerId(this.proxyManagerId.toString())
.build();
}
protected Death createDeathPayload() {
return Death.newBuilder().setSender(createProxyInfo()).setReason(DeathReason.SHUTDOWN).build();
}
protected Heartbeat createHeartbeatPayload() {
return Heartbeat.newBuilder()
.setSender(createProxyInfo())
.setOnlinePlayersCount(platform.localOnlinePlayers())
.build();
}
protected abstract void publishDeathPayload();
protected abstract void publishHeartbeatPayload();
public abstract void init();
public abstract void close();
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright (c) 2026-present ValioBungee contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU GENERAL PUBLIC LICENSE Version 3
* which accompanies this distribution, and is available at
* https://www.gnu.org/licenses/gpl-3.0.txt
*/
package net.limework.valiobungee.core;
public interface ValioBungeePlatform {
int localOnlinePlayers();
String platformProxyVendor();
String networkId();
String proxyId();
ProxyManager proxyManager();
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2026-present ValioBungee contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU GENERAL PUBLIC LICENSE Version 3
* which accompanies this distribution, and is available at
* https://www.gnu.org/licenses/gpl-3.0.txt
*/
package net.limework.valiobungee.core.api.impl;
import net.limework.valiobungee.api.NetworkProxy;
import net.limework.valiobungee.core.ValioBungeePlatform;
public class ImplNetworkProxy implements NetworkProxy {
private final ValioBungeePlatform platform;
private final String proxyId;
public ImplNetworkProxy(ValioBungeePlatform platform, String proxyId) {
this.platform = platform;
this.proxyId = proxyId;
}
@Override
public String proxyId() {
return this.proxyId;
}
@Override
public int onlinePlayers() {
return this.platform.proxyManager().onlinePlayersCount(this.proxyId);
}
@Override
public boolean isMe() {
return this.platform.proxyId().equals(proxyId);
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright (c) 2026-present ValioBungee contributors
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU GENERAL PUBLIC LICENSE Version 3
* which accompanies this distribution, and is available at
* https://www.gnu.org/licenses/gpl-3.0.txt
*/
package net.limework.valiobungee.core.util.logging;
import org.slf4j.Logger;
public class LogProviderFactory {
private static Logger instance;
public static void register(Logger logger) {
if (instance != null) throw new IllegalStateException("Logger already registered");
instance = logger;
}
public static Logger get() {
if (instance == null) throw new IllegalStateException("No logger registered");
return instance;
}
}

View File

@@ -0,0 +1,28 @@
syntax = "proto3";
package net.limework.valiobungee.core.proto.messages;
option java_multiple_files = true;
message ProxyInfo {
string proxy_id = 1; // UNIQUE ID
string proxy_manager_id = 2; // ProxyManager id. changes every reboot, used to detect duplicate proxy names
}
// heartbeat ._.?
message Heartbeat {
ProxyInfo sender = 1;
int32 online_players_count = 2;
}
// death
enum DeathReason {
UNSPECIFIED = 0; // TESTING ONLY
RELOAD = 1; // plugin reload
SHUTDOWN = 2; // proxy shutdown
}
message Death {
ProxyInfo sender = 1; // proxy that died
DeathReason reason = 2;
}