#velocity-dev
164 messages · Page 4 of 1
I suggests multiple improvements: 1.) Make your code object-oriented rather than relying on static access 2.) Name methods according to the casing convention 3.) Don't call the getPlayer method twice. Calling it twice and assuming the value is the same opens you up to a race condition if the Player disconnects asynchronously, although improbable.
iirc you can do getPlayer(args[0]).ifPresent(player -> { .... });

How do you use the config.yml with this
You can use any config library of your choice. Velocity includes the Configurate library for simple plugins tou se
How i can send players in my own plugin to another server from the spigot side?
the same way like in bungeecord?
Yes. Exactly the same way as bungeecord.
Is there a doc for it
And velocity already has it built in?
"Built-in" in the sense that it's packaged in the jar, yes
Do i need to add it as a dependency
No, I don't think
How do I use the Path thing
I have the dataDirectory saved, how do I add the config.conf to the path
You'll be better off reading the Configurate wiki, and further questions on that are best suited in the SpongePowered discord
Why is it so hard to use configs in this
you'll love it when you get used to it
i thought the same thing when i wrote my first project using the velocity api
i love velocity!
how i can connect player to server on login?
uh, well yeah you can use the config too
i assumed you were asking from a dev perspective as we are in the dev channel
i want to do that players are connecting to a random server
in my case lobby
because in config they are joining first lobby01, when is full, offline or sth then are joining lobby02
You write the logic for that yourself
Check which servers aren't full. Get a random number between that amount and boom profit
and how i can get RegisteredServer?
Whats the equivalence of ChatColor.translateColorCodes
legacy component serializer
well, it's your code you can program whatever way is possible
but velocity uses components, so the legacy component serializer is the best method for turning legacy formatting into components
there's plenty of methods on ProxyServer that allow you to get/create registered servers
ProxyServer#getServer i can't remember the name correctly but it's something like that
and you give the server name
At which point during a player being transferred from server A to server B is the connection to server A severed?
Found it, TransitionSessionHandler
Event-wise it’d be ServerConnectedEvent
It’s kept open till the join game packet from the new server is received
Till then the new connection can be aborted
Not event-wise, doing custom fork things
Well alright, as long as you follow the license feel free to ask more technical questions
@night saddle are you making a custom backend <> proxy forwarding system /w encryption?

I'm not sure what you're asking
Someone told me you’re tinkering around with a custom data exchange and encryption between server and proxy
Whatever the case may be you may be interested in tinkering around with the login plugin message api in velocity
Also since you touched the transition handler; fair warning, if you intercept packets or disconnect the old server quicker you may have to send a fake Respawn/join game combination to keep the client from getting stuck
I lost track of how much time it took me to debug that mess
That was my old solution to the problem
My solution now is to use redisson locks & such
Hello, I have
@Subscribe
public void onPluginMessage(PluginMessageEvent e) {
System.out.println(e.getSource());
}
}```
And it does not work
As I read from the docs, it should be fired when a plugin sends message to the player, am I wrong?
Never mind, I am wrong, another question, is there any event related to the messages send from proxy to the player?
Messages as in chat?
I have a .toml file in my resources, but it isn't being loaded, is there something I have to do to load it? If I create it and put content in it I can load the file, but it's not creating one just by being in the resource folder
Yes, I assume it isn't possible without aditional protocol API?
?
check if null first?
whats the error now
Same error
do a clean build
Why would that make a difference?
to make sure you're actually running the new jar cus the error shouldn't be the same with that afaik
Clean build same error
Please send large files/logs to a pastebin
A sensible, modern pastebin. Share text and source code snippets with no hassle.
I'm printing the Exception response message
Not much to show that screenshot is the entire thing
You're not showing the location where you call String.length()
As the message states, length() is a method you tried to call on a null String
Pastebin.com is the number one paste tool since 2002. Pastebin is a website where you can store text online for a set period of time.
This is different from accessing the length field of an array
config.getString is returning null, most likely
Let me see
Add Objects.requireNonNull to your Common.sendMessage method. That'll make matters absolutely clear
Show Common.sendMessage
Add Objects.requireNonNull(message, "message") to this method
Add that at the beginning of the method
I don't know how to use that
source.sendMessage(LegacyComponentSerializer.legacyAmpersand().deserialize(message));
I.e.:
public static void sendMessage(CommandSource source, String message) {
Objects.requireNonNull(message, "message");
source.sendMessage(...);
}
Then run your code and this will allow you to determine whether message is null
Right
message not error
Don't catch and log exceptions. Simply let them be thrown
Caused by: java.lang.NullPointerException: message
Exceptions are your friends; they tell you what went wrong in your code, where, and often why. If you hide exceptions, you'll spend more time debugging and more time annoyed at your code because it doesn't work. If you let the exceptions show themselves, you'll find out the cause of the bug much more quickly and your software will become more robust overall.
Congratulations, now you know how to use Objects.requireNonNull to fail-fast in your code
Indeed, config.getString is returning null
Why can't papermc make a simple config system
I spent all name writing a toml handler
and now i must fix it
It isn't an issue of papermc
I know
just learn to use configurate
Never
If you don't want to use configurate, there are other configuration libraries you can choose from
Yeah I'm using toml
I don't use Configurate either
See configurate sucks
It doesn't you just don't know how to use it
You guys don't even have a tutorial on it because even you guys can't figure it out
I don't think that's true
Did you look at the wiki?
The velocity one yes
they even have written code examples for more involved issues
Regardless, if you want to use a different configuration library, I can suggest to you DazzleConf: https://github.com/A248/DazzleConf
this seems to be a lack of research: https://github.com/SpongePowered/Configurate/wiki
I love how you always recommend your own stuff
You made something for every scenario
I'm not the only one who uses it: you can look at who else does
a248 does make some nice libs
Do you just shade dazzleconf?
Thanks for the compliment, Allen
Yes, you would, like any other library. Though I should mention this isn't the place to seek support for DazzleConf (whereas Configurate is suggested by the Velocity API so it makes sense to ask here)
They tell me not to ask about configurate here
Probably because they can't understand it either
Oh right, you should ask in the SpongePowered discord
They have a dedicated channel for Configurate there
how do i make the tab completion with BrigadierCommand match things that start with the current text you typed for the argument, instead of everything? i don't like how it shows everything no matter what text i typed because if i try and tab it in, it replaces it with something comopletely random
There is a suggest function you can implement if I’m not mistaken
You’d probably want to use that, but I might be wrong. Haven’t used brigadier much myself
i think that's what i'm using right now, here ill try and get an example snippet from one of my commands
https://github.com/bobacraft/bobawhitelist/blob/ac100df2082328db3ac46455c1371b0c6a4bd513/src/main/java/best/boba/bobawhitelist/WhitelistCommand.java#L76-L82 here's one of my commands in my plugin
I did something like this
the way you are doing it is just adding suggestions, not filtering them
hmm ill try that
i tried something like this, but context.getInput() gives the entire command instead of just the last argument. am i missing something?
Uh, I am not sure. I wrote all of this almost a year ago so I am not sure if something has changed
hm i wonder why it works in yours then
i guess ill just split it and get the last string after a space
~~I would suggest using the node you built and looking it up via https://github.com/Mojang/brigadier/blob/cf754c4ef654160dca946889c11941634c5db3d5/src/main/java/com/mojang/brigadier/context/CommandContext.java#L140 instead of manually parsing the string. If it's an argument node, prefer https://github.com/Mojang/brigadier/blob/cf754c4ef654160dca946889c11941634c5db3d5/src/main/java/com/mojang/brigadier/context/CommandContext.java#L81~~
I think this code is broken, has the filtering really worked in the past?
Forget everything I said above, use SuggestionsBuilder#getRemaining()
Iirc that string should only contain the argument for which suggestions are being computed
if I do /command it gives me the correct options for args0 and args1 if I don't type anything in, If I type anything in args0 for example /command g
it will show the options for args1
OK I'll try that
It worked
is there something like PlayerChatEvent for player deaths / advancement type messages?
event.getMessage().equals("has died");
smth like that
i assume you mean in the handler for PlayerChatEvent, that event isnt triggered for deaths or advancements
What is the default configuration file system implemented in velocity ?
Toml ? Yaml ? Both works
Will there be a deprecated one in the future ?
Or a preferred one ?
There is no default
It is recommended to use sponge's configurate.
Personally I just use kotlin's JSON but I will probably switch to configurate.
@vestal prawn
Never used either so
¯\_(ツ)_/¯
As I said, I'm just using kotlin's json
Because it loads into a fancy config data class which is easy to read from.
Can you give some examples of input->suggestions? I don't understand what you mean by e.g. "options for args1"
The code should do /command->fileSuggestions, /command something->suggestions, /command foo b->stream, /command a b c->(empty list)
Unless there's a trailing whitespace, in which case Velocity will add an extra empty string to the args array
I do /command then its suppose to suggest test1 or test2 which it does if i type in no letters and just hit space after /command. Then if i do /command test1 it will give me options for test3 or test4. So /command test1 test3. But if I do /command and type t so it looks like /command t it will suggest test2 and test3
In /command test1 is there a trailing whitespace char?
I can't understand what test1, etc refer to since I don't know the actual variables. Can you share those instead?
Test1 = config.yml test2 = messages.yml test3 = get test4 = set
So if i start typing /command config.y it will suggest get and set
But if i dont type anything after /command it suggests config.yml and messages.yml
hello! i'm working on porting a plugin from bungee to velocity, most of it's been fine, but i'm having an issue with a LoginEvent listener, what i'm trying to do is cancel the event if the player is banned using event.setResult(ResultedEvent.ComponentResult.denied(VelocityServer.formatMessage(message.toString()))); that doesn't seem to do anything though, i still connect normally while banned and it doesn't stop me from joining
That's intended behavior: when you have 0 args you have /command. Once you have /command config.y, you have 1 arg (hence your method returns suggestions instead of fileSuggestions)
Ah alright
All I can say is make sure that method is actually called
I use the exact same method in LibertyBans (another ban plugin)
the listeners method? should be called in the main class
Add a debug statement or use a debugger to confirm
that would be a good idea, will check that
and yep, the event is not getting fired
hmm, why isn't it though, the listener should be registered and everything seems to be right?
you need to use something like this : ClassLoader#getResourceAsStream(<path>), to first copy your file and then read it
in the constructor of your plugin i believe, i never did that but i know it's how we do it
i think you need to first get your file with the method above, and then you can copy it to your plugin folder (if the config file was not here) and then read the config file
You can access those resources at any time
How?
getResourceAsStream returns an InputStream
an InputStream is merely a stream of data. If you want to write it to a file, you have to do that
you should always use try-with-resources when working with InputStreams
Yes, so do what I suggested
You can use Files.copy(stream, destination) to copy an input stream to a file path
private void LoadFile(String path) {
File filePath = new File(getDataFolder().toFile(), path);
try (InputStream input = Tailop.class.getResourceAsStream("/" + filePath.getName())) {
if (input != null) {
Files.copy(input, filePath.toPath());
} else {
filePath.createNewFile();
}
} catch (IOException exception) {
Common.log(getClass().getName() + " [TAILOP] [ERROR] " + exception.getLocalizedMessage());
}
}
Look right?
input always equals null
Rethrow exceptions rather than swallowing them - throw new UncheckedIOException(exception)
Then the parameter you passed to getResourceAsStream doesn't exist as a resource
Could it be the "/"
I've seen that before
What is filePath.getName() returning? Find out
Also, I don't know why you're converting to and from java.io.File when you could much more easily use Path.
I'm more familiar with C#
getDataFolder() returns a Path and Files.copy uses a Path so using a Path will avoid the need to use toFile() and toPath()
More similiar to it
This is Java, not C#
Are you sure it exists inside the jar, then?
How would I check
Yes its in it
Could it be the filepath?
Its trying to load it from ./Server/plugins/tailop/work.yml
you aren't creating the folder for it
If filePath.getName() returns work.yml then it will work as expected
So, is this true or false?
Not copying whats inside
huh
work.yml is not the same as this expanded path
private void LoadFile(String path) {
File filePath = new File(getDataFolder().toFile(), path);
try (InputStream input = Tailop.class.getResourceAsStream(File.separator + filePath.getName())) {
if (input != null) {
Common.log("CALLED");
Files.copy(input, filePath.toPath());
} else {
Common.log("NEW FILE " + filePath.getName());
filePath.createNewFile();
}
} catch (IOException exception) {
Common.log(getClass().getName() + " [TAILOP] [ERROR] " + exception.getLocalizedMessage());
}
}
Don't use File.separator
What is the correct path
Bruh
I asked and you said yes
Yes, / is right, and the resource should be something like /config.toml
Those are both true statements
However, the resource path should not be ./Server/plugins or any such expanded path
File filePath = new File(getDataFolder().toFile(), path);
path = file name
so it should be:
File filePath = new File(path);
Use path instead of filePath.getName()
No, qualifying by the data folder is correct
I really do suggest you use Path instead of File; there's really no reason to use File for new code
How do you create a path
getDataFolder().resolve(path) gives you the file you're looking for
How would I add the file onto it
oh nvm
Alright using path, now what
private void LoadFile(String path) {
Path filePath = getDataFolder().resolve(path);
try (InputStream input = Tailop.class.getResourceAsStream("/" + filePath)) {
if (input != null) {
Common.log("CALLED");
Files.copy(input, filePath);
} else {
Common.log("NEW FILE " + filePath);
new File(String.valueOf(filePath)).createNewFile();
}
} catch (IOException exception) {
Common.log(getClass().getName() + " [TAILOP] [ERROR] " + exception.getLocalizedMessage());
}
}
getResourceAsStream("/config.toml") will do what you want
Instead of "/" + filePath (which will include the data folder), you need to use "/" + path
The jar doesn't know anything about the data folder; getResourceAsStream fetches a resource from the jar
No, I didn't, if you read closely
just to be clear: you told me this, which would imply that you had the right path
It returns work.yml
Ok it's working now
For the suggest command, /command arg1, arg2, arg3 what would be the right args[number] for arg3
I believe 2
I've grappled with tab completion many times over, so I'm not the best at it
If the argument after arg3 is tab-completed, I believe the last element in the argument array will be an empty string
bump
Have you overcome this? It would seem your question has changed since your original inquiry
still the same issue, but that might be why its happening
no idea whats causing it though
Are you sure the line registering the listener is called?
yep
Are you sure the initial line where you receive the event is called?
yes, it is definitely being called
ah, that just showed up, that would be why
yep and now it works, i was just blind lol
how to check if initialserver is online?
is there a ping method?
cos, basically, pinging it or just trying to connect and dealing with it failing is the only real way to check if something is accepting connections
Does Snakeyaml have a support discord
Is there a better way to get UUIDs of offline players other than querying the mojang api and then caching it in my plugin directory? I know on bukkit/spigot there's Server#getOfflinePlayer(String), but is there an equivalent for velocity?
No
No
If you use luckperms you can use their cache
Or cache the username until the player connects you get their uuid
Getting this in console:
[13:56:52 INFO] [tailop]: [motd=&e&lThe best server
&aJoin us now!, hide_player_count=false, max_players=+1000]
[13:56:52 ERROR]: Couldn't pass ProxyPingEvent to tailop
java.lang.NullPointerException: Cannot invoke "java.util.Map.entrySet()" because "data" is null
at org.tailop.util.config.YamlConfiguration.getString(YamlConfiguration.java:80) ~[?:?]
at org.tailop.events.ServerHandler.onServerPing(ServerHandler.java:53) ~[?:?]
at org.tailop.events.Lmbda$7.execute(Unknown Source) ~[?:?]
at com.velocitypowered.proxy.event.UntargetedEventHandler$VoidHandler.lambda$buildHandler$0(UntargetedEventHandler.java:47) ~[Velocity.jar:3.1.2-SNAPSHOT (git-b8f1df44-b110)]
at com.velocitypowered.proxy.event.VelocityEventManager.fire(VelocityEventManager.java:598) ~[Velocity.jar:3.1.2-SNAPSHOT (git-b8f1df44-b110)]
at com.velocitypowered.proxy.event.VelocityEventManager.lambda$fire$5(VelocityEventManager.java:479) ~[Velocity.jar:3.1.2-SNAPSHOT (git-b8f1df44-b110)]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[?:?]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[?:?]
at java.lang.Thread.run(Thread.java:833) [?:?]
this is the code
78 public String getString(String path) {
79 Map<String, Object> data = this.yaml.load(this.inputStream);
80 Common.log(data.entrySet().toString());
81 return String.valueOf(data.getOrDefault(path, null));
83 }
do more debugging
What?
inputstream, yaml etc
Well something is null
It's being called in a ProxyPingEvent
When its called through anything else it works fine it's weird
And I checked how its being called over and over nothing is wrong with it
How to suggest player a tab completion with two different completion lists like
/rank {player1, player233, player404} {player, staff, admin}
Ah, I'll try and do that then
Something like Luckperms#getUserManager()#getUser(UUID)
I can't remember exactly
and then User#getName
something like this
an input stream can only be consumed once. You should load the configuration once and re-use the values, rather than loading the configuration every time you fetch a value
Ah alright
Worked thanks
@wary tiger are you familiar with snakeyml at all? I'm trying to make it so if you put a . in the key for example t1.t2 and value of test
it would look like
t1:
t2: test
Yes, I'm familiar with it. I've used the snakeyaml Node API before
Oh I see, yes
I think you want to interpret dotted key paths and fetch the corresponding config value, right?
Yea
A configuration is really a Map of Maps
Thats what I figured I would have to do
Was just wondering if there was a built in way
The top-level configuration is a Map<String, Object> -- you can cast to that and then retrieve values as needed
Not in snakeyaml. you need to write code to expand the dotted key, then recursively fetch the value
(you don't necessarily need to use recursion but it's probably easy to do it that way)
So pretty much Map<String, Map<String, Object>>
yes, that's about right
Keep in mind: normal config options (which aren't further configuration sections) won't be Maps
I don't need them
Imagine:
Map.of("integer-value", 1, "section", Map.of())
A Map of Maps, but the values can be either Maps or configuration values
The Bukkit config library uses snakeyaml, but it also uses a lot of bad practices which I don't recommend, such as swallowing exceptions
Is it open source?
yes
Where can I find it? Its not on their github
you can use your IDE to view the sources for the paper API
In terms of the broader objective, I wonder why you do not use a simple configuration library which handles these semantics for you
configurate ❤️
Never
I mean I’ve seen people shade the bungee-config module
It’s entirely possible but I advise against it

Do you advise against it because you are a paper dev or an actual reason
I advise against it because there are some things it requires which aren’t in that module. It may work fine but it may also just blow up
an other actual reason is that bungee-config is a poorly designed configuration library, similar to bukkit's
Just don’t
Good luck relocating everything or you’ll get overlapping
Also Lombok processing will take a literal year
No
why...
If you’re willing to get it up to feature-parity with configurate and are willing to maintain it then we are able to consider it
What features does configurate have that makes it superior to snakeyaml
Not being just yml
??
(Amongst others)
object mapping, config generation, hocon support, proper comment support...
Config generation?
I think it also has a basic versioning system for changing the config
It does. Configurate is just a framework system that has quite a few supported config adapters
Gson, hocon, yaml and a few more
There is also a way to constrain values intelligently that I’ve never touched
What's config generation?
i.e. I have a class (I use kotlin, but same works for java) @ConfigSerializable data class ConfigExample(var option1: Int = 69, var otherOption = "hehe", var other: SomeClassWith@ConfigSerializable? = null). I call a few methods, and it spits that out as a config
Just creating the file? Or does it generate parameters
Ah
I mean
thats pretty simple to do though
snakeyaml you can do that
sorta
Actually yeah if you have to call a few methods then snakeyaml does the same thing
A good configuration library will do that for you; the difference is how
Real example: Here's a config class https://f.u11.io/vPb453. I load it with my little wrapper, which saves it to trainDestroy.conf https://f.u11.io/NKO4pM , then I can just grab that (config().firstRun) https://f.u11.io/tHZsHD
Do data classes generate setters?
in that case it's all immutable, but mutable configs are ok too
(val (immutable) vs var (mutable))
this is kotlin
but muh extension and operator functions
How do you get an offline player by their uuid
You don’t. If you need data then you either need to cache it or pull it from the mojang api
No; you need to do HTTP requests to it.
You can find the endpoints on wiki.vg
It's a different kind of API
public static CompletableFuture<User[]> queryMojangApi(AsyncHttpClient client, String[] username) {
Preconditions.checkNotNull(username, "username");
Preconditions.checkNotNull(client, "HttpClient");
Preconditions.checkArgument(username.length > 0, "no username");
Preconditions.checkArgument(username.length <= 10, "too many usernames");
CompletableFuture<User[]> ret = new CompletableFuture<User[]>();
String dataPost = GSON.toJson(username, String[].class);
client.preparePost(PROFILE_ENDPOINT_MULTI_POST).setBody(dataPost).execute()
.toCompletableFuture().thenAcceptAsync(result -> {
if(result.getStatusCode() == 200) {
try {
JsonObject[] profiles = GSON.fromJson(
result.getResponseBody(StandardCharsets.UTF_8), JsonObject[].class);
User[] data = new User[username.length];
for(JsonObject profile : profiles) {
User contained = fromJsonObject(profile);
for (int i = 0; i < username.length; i++) {
if (username[i].equalsIgnoreCase(contained.getName())) {
data[i] = contained;
break;
}
}
}
ret.complete(data);
} catch (Exception e) {
ret.completeExceptionally(new IllegalStateException("Invalid API response", e));
}
} else {
ret.completeExceptionally(new IllegalStateException(
"API returned status code " + result.getStatusCode()));
}
}).exceptionally(throwable -> {
ret.completeExceptionally(new RuntimeException("Bad request", throwable));
return null;
});
return ret;
}
That’s roughly how I did username > UUID
The user class is just a record class with the properties the api provides
nvm
This is just an example. Do ask if you have questions. That snippet can be treated as under the unlicense license
Hey, how do I make it so switching between servers it doesn't unload then load the texture pack? If one of the servers is 1.12 on the network and I join it from a 1.17 server, it keeps the texture pack and no unload screen or load screen shows even tho that server doesn't have the texture pack in the settings, but if I join the 1.17 server from the 1.12 server it unloads the texture pack and loads it again
If I then go from 1.12 to a 1.12 I also get no loading screen either, even tho I'm on the same mc version
That’s because the older server with ViaVersion ignores a few things the client sends. I don’t have an exact list of what you’d have to change to make it behave in that way, but it’s impractical to do on velocity
Actually after more testing, it seems the loading screen and unloading screen only happens when I return back to the server the texture pack is set on
I just went from a 1.17 -> 1.12 -> 1.12 -> 1.18 -> 1.17 (a different one to the original) then back to the original and it only did a loading screen when going to the one its hosting on
Oh, that.
but I had the texture pack on all the others
I've been trying to get this behavior for months and I've randomly come across it, but I'd prefer if there wasn't an unloading and reloading when going back to the original
Does via version work in velocity?
Well, it wouldn’t (shouldn’t) do that if you provided the hash for the resource pack
Yes.
The hash is provided
A correct sha1 byte hash?
Some features of Via don’t work while installed on a proxy because the plugin doesn’t have information about the player context (which the server would have)
Yeah I used mcpack to host and generate it
I know but does it mean like sword blocking?
Yes. Ladders and lilypads and potions are the same deal
They just don’t work correctly
So its better to just add to each individual server?
If you open an issue on GitHub I’ll take a shot at fixing it, but no guarantees. I assume I know how I can stop it doing that, but that’s just an educated guess
I can show you it live on my network if you want? Dm me
I actually want it to work like this though just not reset it when joining the original server which set the resource pack, is there a way to do that?
No, I know why this happens. The resource pack queue velocity has causes it, but to fix it I have to change a few things that might break older clients
Can you point me to where in velocity handles this?
thanks
There is a bug in that logic already that should be fixed in the PR by @oblique haven
I just don’t have time for all of this till mid February
wave
my PR fixes it, but my PR isn't done
still have to add one more line of code
Well I actually kinda want it as a feature, I have a server setup in multiple zones and I don't want the texture pack loading and unloading screen each time they switch zones
That’s not the issue I was referring to

But it’s fine
PRs to this are welcome
you're talking about the Origin issue right
Yea
Whats that?
The proxy is incorrectly swallowing responses
Oh actually
That might be the issue you have

Why didn’t I think of that before
I see how it could be
Okay, yea, no that’s not the issue
@latent kelp the re-loading screen happens because the server re-sends the apply-resource-pack request each time you join
but you only want to send it once when the player first joins right?
hash doesn’t matter on newer versions, it only does for downloads. It will respond by re-applying it anyway
Correct, I don't want it to resend once a player first joins unless they join a different server in the network which has a different resource pack, but from my debugging I can check if previousResourceResponse is true and then if it is just don't send the resource pack to outstanding, I could then add a check to see if its different from the origin
or you could add a check to see if the hash of the previousResourcePack.equals(newResourcePack) and if true send the appropriate response to the server and swallow the request
that’s what I’d do on velocity to “fix” it
Can you use a velocity api in a paper plugin?
wdym?
like i have my core as a velocity plugin, but need to access it from plugins running off paper
Core is on velocity server
need to access it from paper servers
Do you only need to access features of it when a player is connected?
Yes
The core is storing all player data
Just need to call it and get the player data when they do stuff
In that case you could create your own plugin messaging channel communication
Is it the same for velocity as bungee
Since the proxy and backend server are different processes, you can't call methods directly in one from another
basically, https://gist.github.com/Xernium/95c9262c5f70b8791557861bbc09be1b see this as an example
I might just make a server to handle it
Because it's just storing player data
But needs to be global
Yep, I check if the previous response isn't null and is true, if so just ignore it and if its a different hash I'll allow it 🙂 neat!
careful however, you’ll need to send the appropriate responses in correct order back to the server
ACCEPT, DOWNLOAD, SUCCESS
I might be mistaken here but I think DOWNLOAD is still sent even if the hash matches
@oblique haven you know this by chance?
otherwise its going on my to-do-list
Yeah this is the perfect behavior I wanted, I'd assume you guys are going to put a patch in to fix it so it resets each time tho, I'll just have to fork it and make sure those changes don't go live
Hmm? I am fine with not re-sending this if the hash equals to the currently applied resource-pack. Its a quality-of-life fix and I dont see it causing issues, so it will probably be included in the row of changes FiXed proposed
Yeah that would be good, currently the other servers don't have the texture pack set to anything, I guess my fix for now could see if the texture pack which its trying to queue hash matches the previous one and then just swallow it so it doesn't load it, but if that could be added, that would be great 🙂
I should also maybe add API to force-clear resource packs
all good, just send the appropriate responses back to the server if you’re swallowing it or the server will never get the note you have the pack applied.
To check if a server exists would I do if(Main.getServer().getServer(args[1]) != null)
https://jd.velocitypowered.com/3.0.0/com/velocitypowered/api/proxy/ProxyServer.html#getServer(java.lang.String) is what you’re using? Here you’d do:
Optional<RegisteredServer> server = Main.getServer().getServer(args[1]);
if(server.isPresent()){
RegisteredServer actualServer = server.get();
}
don’t use .getServer(name) twice. Do it once and store the result. In the time that happens it might no longer be present
I know that was just an example
bump
No. Velocity doesn’t intercept messages from the server
is there any way i could get those messages? maybe a plugin on the servers?
Yes that would work
You’d be ideally be using a custom plugin channel to get that from server to proxy
are there any examples on how to do that from paper > velocity and/or fabric > velocity
For Paper > Velocity, you can check the Spigot wiki on the BungeeCord messaging API (replace the BungeeCord channel by your own)
Or if you prefer it, check the javadoc on Player#sendPluginMessage iirc
Audience.audience(
EssentialsPlugin.getProxyServer().getConsoleCommandSource(),
EssentialsPlugin.getProxyServer()
).sendMessage(component);
Why can only players see the message sent like this, but the console will not display it?
I'm pretty sure that when I use EssentialsPlugin.getProxyServer().getConsoleCommandSource().sendMessage(component); alone, the console can display
Is this a bug? Or is the design so?
I'm looking for an suggestion of how to do a system,
right now we run many instances of the same game mode and players connect to others through world borders, there's a combat system in which if a player quits while in combat he dies, even tho while crossing the border would also be playerquitevent, is there a "channel", in Bukkit, but related to Velocity, where I can detect when a player is disconnecting from the server with his client? and not being sent to another server.
Please if you could give an suggestions, i'd be grateful, ping me when answering
Have a great day y'all
I'm also able to implement a redis channel, would the connection event to another server be called before PlayerQuitEvent?
If you’re handling the world switching you might wanna try sending signals to the server they’re on before switching so like
Proxy -> server (signal)
Server -> proxy (understands)
Proxy disconnects player
Server doesn’t kill them since they saw the signal
That’s probably a decent way to handle it
How can I make velocity refresh available commands for a player?
Its a permission plugin. Need to refresh them after changing the group of a player.
Can declare commands fire more than once to the client? If so that’d be how
vyt: not without the server doing it
fixed: yes, on server change
I would agree with you, but I am 95% sure there is a precedent in vanilla for doing just that
Go onto a vanilla server and /op yourself. Now you have access to more commands
I am just not entirely sure how that works on Velocity atm
I figure it would just send a new declare
Like when permissions update, re-send declare maybe
It would need to work very differently in Velocity if it were to work
Yes, but is there a pre-disconnect event? also ->
A test i could run to see if the order runs the way i need
Is there a prorblem with trying System.currentTimeMillis() whene DisconnectEvent happens and when PlayerQuitEvent happens?
Should there be an margin error?
I mean I’d think the server would announce the event first but I have no idea
Since the proxy holds the connection it could go either way, I’d say test it
Disconnect is expected to always occur after the connection is already established successfully to the new server
If you want an easy solution, try to initiate the switch from the backend server (save the data, then send the request to the proxy). Otherwise it will require more complex locking logic
There's currently no way to do this since Velocity would have to keep track of the backend server's command tree, which requires a lot of memory
It was discussed in the past, but we decided the permission changes are not a usual occurrence, and a server switch will send an updated tree anyways.
Hey I'm starting my first Velocity plugin. Does anyone have any resources on using com.velocitypowered.api.proxy.messages? If I understand correctly, this could be used for back and forth communication between a Velocity plugin and say, a Paper plugin? also is this the best approach to do that? Does anyone have any tips? p.s. i have never worked with any other Minecraft proxy servers prior.
^ disregard. i scrolled up and found some info. feel free to share any tips tho
Which API version should I be using?
ok ty
Hey. I am developing a velocity plugin using IntelliJ IDEA. Does someone have a working configuration for a test environment? I really would appreciate it. 🙂
A working configuration of what?
I want to simply run my plugin together with velocity without any manual work
I mean the default config works fine
If anything it takes like 5 minutes to configure
But you can just use the default
Any RedisVelocity plugin existing?
I expressed myself unclearly. I am looking for a way to automatically build my plugin and start/restart the velocity server which loads my plugin.
Yeah, Chocolate and Trevor
Is there a way to intercept/listen to player chat packets using the velocity API or a third party plugin? (like with ProtocolLib)
I just created my own gradle task for easy plugin testing. Maybe this is helpful for someone 🙂
plugins {
id "de.undercouch.download" version "4.1.2"
//...
}
//...
task runWithVelocity() {
download {
src 'https://papermc.io/api/v2/projects/velocity/versions/3.1.1/builds/98/downloads/velocity-3.1.1-98.jar'
dest layout.buildDirectory.dir('server')
overwrite false
}
copy {
from layout.buildDirectory.dir('libs')
into layout.buildDirectory.dir('server/plugins')
include "*.jar"
}
javaexec {
classpath layout.buildDirectory.file('server/velocity-3.1.1-98.jar')
workingDir layout.buildDirectory.dir('server')
}
}
Velocity-api is on the papermc repo. You shouldn’t be attempting to use the full -jar unless you’re trying to mess around in the internals
Also, the intellij plugin here https://plugins.jetbrains.com/plugin/8327-minecraft-development is very useful for creating baseline projects with basic features for your platform, I highly recommend it
Maybe with Protocolize, but we dont support that.
wait
player chat packets?
my bad, yes there is a chat event
But does it include every packet? I need all chat messages sent to players not just the player chat messages. System events from plugins as well.
that isn’t supported. Velocity doesn’t intercept those. For those you’d need to use something like Protocolize
you can intercept player -> server with the api but not the other way around
Ok thank you. I will take a look at Protocolize
I guess the idea is to run and debug plugin and potentially server, so api only would be unconvenient
~~```
Could not find com.velocitypowered:velocity-api:3.1.1.
Searched in the following locations:
- https://nexus.velocitypowered.com/repository/maven-public/com/velocitypowered/velocity-api/3.1.1/velocity-api-3.1.1.pom
If the artifact you are trying to retrieve can be found in the repository but without metadata in 'Maven POM' format, you need to adjust the 'metadataSources { ... }' of the repository declaration.
?
Seems to work correctly if I get it from https://papermc.io/repo/repository/maven-public/
I thought the old velocity nexus was supposed to be deprecated; it should redirect to the paper repo mentioned above
I’ll investigate later
I used the minecraft-dev plugin for IntelliJ CE to create my velocity plugin. I am using Maven. I am getting these errors in my pom.xml. Does anyone know how I could resolve them?
What is causing this:
error.log by @ashen fiber: https://paste.gg/b72727a78d5a438ea078da65e2ba8f20
plugin tried to create a class with an invalid class name
Illegal class name "/Lmbda$4" i think
The plugin is creating it
the plugin did something which caused velocity to do something which caused an error
class MessageOnJoin @Inject constructor(server: ProxyServer, logger: Logger, @DataDirectory val dataDirectory: Path) {
var joinMessage: Component? = null
fun loadJoinMessage() {
val joinMessageFile = dataDirectory.resolveSibling("joinMessage.txt")
if (!joinMessageFile.exists()) {
joinMessageFile.createFile()
joinMessageFile.writeText("Welcome to the server! This is the default join message. You can change it by editing the file joinMessage.txt in the data folder.")
}
joinMessage = get().parse(joinMessageFile.readText())
}
init { loadJoinMessage() }
@Subscribe
fun onConfigReload(event: ProxyReloadEvent): EventTask = EventTask.async { loadJoinMessage() }
@Subscribe
fun onPlayerJoin(event: ServerConnectedEvent): EventTask = EventTask.async { event.player.sendMessage(joinMessage!!) }
}
Where is the issue... I see no issue anywhere.
I'll admit i dont understand Kotlin but does this look right .sendMessage(joinMessage!!). What does the exclamations mean?
Kotlin implements Null Safety.
If a variables type definition has a ? at the end that means it could be null.
If a variables accessor has a ? that means only do it if it is not null.
If a variables accessor has a !! that means throw a NullPointerException if it is null.
ahh that's cool. it all looks foreign to me haha
Really that should be an IllegalStateException, because it should be impossible for that value to be null at that point.
Which I would do with joinMessage ?: throw IllegalStateException()
?: basically means "if its null, do this"
interesting
Kotlin has all sorts of cool syntax things that I quite like
Like extension functions.
fun CommandSource.sendMiniMessage(message: String) = sendMessage(MiniMessage.get().parse(message.trimIndent()))
It allows me to "add" my own functions and variables to classes that I don't control.
That extension "adds" a function to CommandSource which basically just hides away the nonsense of constructing a Component with MiniMessage.
So instead of doing
player.sendMessage(MiniMessage.get().parse("<red>Test"))
I can just do
player.sendMiniMessage("<red>Test")
Which is much nicer.
hmmm.. i still able to build my plugin even though my pom.xml is spitting out errors. i guess if it ain't broke, don't fix lol. But i do need to add some dependencies so we'll see
So in my Velocity plugin, I have a map of online players where each player points to a PlayerData class with some variables. I need to access these per player variables from the backend-servers as well, sometimes quite frequently (mainly every PlayerDeathEvent and every time a player is damaged by another player). I don't quite understand the limitations of the plugin message API. Would this be an appropriate use of the plugin message API or should I be using something like Redis or memcached instead?
how do i get the UUID of the player executing a SimpleCommand?
ah ty
I'm still trying to wrap my head around plugin messaging. If a plugin message is sent to the server by a player (PluginMessageEvent.getSource() instanceof Player) does that mean they're tampering with packets? Or does it mean one of the backend-server's is sending it but associating a player with it? I'm having a hard time finding docs or resources about this stuff (resorting to looking through github projects)
Source is where the message came from
If it’s Player then it’s most likely that this player is using mods or hacks that are capable of sending a direct message
Generally if you want to use plugin messaging to communicate between proxy <-> server you need to swallow all messages on the same channel by the user
There’s a reason for that:
Normally plugin messages travel like this:
player -> server
and
server -> player
But you have a proxy in between
player -> proxy -> server
and
server -> proxy -> player
This is how the messages travel if you don’t set them to .handled() on the proxy. By setting a message to handled it stops the message on the proxy:
player -> proxy
and
server -> proxy
So always set them to handled no matter where they’re from unless you’re just watching the data going through
If you just want communication between server and proxy then your source should be instanceof ServerConnection
If you want communication between player and proxy then your source should be instanceof Player
Likewise if you want to send a message to the server then your message sink should be the ServerConnection of a player
And if you want to send a plugin message to the user then the message sink should be Player directly
Awesome thank you for the explanation.
I generally kick/punish players that attempt to use an internal plugin channel to try and do something
Because there are no mods that do anything on my channels and the vanilla client will never touch them you can be sure that something fishy is going on if there is suddenly a message source instanceof player

fair enough, good idea i guess to disconnect and prevent arbitrary/unneeded network usage.
Can be both, can be for an example on bukkit p.sendPluginMessage, it uses the player's client to send the messages, so, if i were you using it i'd be very careful, there's some exploits that have already happened like on authme, they had an autologin channel 😂
Redis is more safe
im looking into redis at the moment
Alright great
use JedisPool, dont use Jedis directly
This way you'll have healthy connections and almost no chance to break any system, depending on the conf of redis server
If you need communication in real-time then you’re better off with plugin messaging
Well my problem is.. For each online player, I store their ID in a map along with player data that is loaded from a MySQL database when logging in. I need this player data to be accessible from each server. I could serialize each player's playerdata object and store it in Redis. And then deserialize it when accessing it. But I have an event that checks every time 2 players PVP, if they're allied, it will prevent pvp damage between the players and I'm not sure if it's worth setting all of this up only to find out deserializing the player data object everytime PVP occurs would lag out.
well, it's complicated
If paper already had api for it you could send the data on login to the server
Or there's more fields on the data else than related to pvp
Although, if you want to use the channels
use server.sendpluginmessage
and not p
this way you'll be a little more secured
I use redis on a 400p network on 1.18, I had a way to check if players were online on servers so it was the best way I found
Jedis usually is "real-time" too
Depending on how you use it
You can for sure serialize & deserialize their data, i wouldn't say its the best way tho
One thing is
they need to be able to send (and accept) ally requests cross-server. which is why i want to "globalize" the player data cache using redis.
You of course is not going to call any jedis call on the main-thread.
But then why don't you use jedis pubsub?
The concept of it is like plugin messaging
So they all have access to current valid cached data without constantly querying or polling the mysql database
I’d have done it with a presync login plugin message before login to each server and a plugin message to update data if applicable
Not sure if you really want a redis system for that
ex: every PvP event
yeah, i need to check player's online as well..i feel like it would be a lot of work to setup plugin messages just to read/write a cache.
You'd need to keep track of the cache on both servers updating the object constantly
If you're doing this for the pvp
there's no need for you to keep that cached on another instance where the player is not online
that's why i wanted to have one cache by using Redis
well then what about ally requests.
I would like Player A to be able to send an ally request to Player B (on a different server). and if player B accepts, update both player's player data to reflect that change. then if player B hops on Player A's server, they won't be able to PvP
Keep object cached on actual player instance -> and only there <-
Create your message system using the plugin messaging so you can handle accept, request, deny, and removal of allies through the network while they're online
If you need to check if an player is online on the server, write an online tracker on velocity(create a plugin) that sends plugin message to the servers when a player connects-disconnects of the proxy or prefered instances
So on every data change, you already run a statement to update that on the database
yeah
You won't have any lag issue with this system if you correcttly manage the used threads
there's lots of database reading, not writing. so i only use the cache for reading, and when updating the db i update the cache
and never the main thread on any of those processes.
Why would you say that?
First of all, unless you're calling a read a thousand times on the main thread, you won't have any lag issue
If you want to check if the player is already an ally
Send the ally request
check on the cache if the user is already an ally
if he is
send back an message to the requester, "hey you're already an ally"
Both ways work just fine
There's no problem in using the database that way, unless you're calling it on the main thread
Of course, if you want less requests on the database you'll have to increase the requests on pluginmessaging
If you used redis, you'd be serializing deserialzing data everytime it changes either, you'd just decrease the requests on the database to increase the requests on the redis server
And risk loss of data
the database caching is fine i think. my issue is now being able to access this data real-time from different sources. would it be worth the work to setup a central cache using plugin messaging or use redis. but if i use redis i have (lots of de/serializations) and plugin messaging seems like a hacky way to do this.
it would be nice if i could store a plain java object and access it kind of like a database, just in real-time.
It would be worth to run a query to get the data from the database and check what you need to check
You don't need a central cache
It wouldnt be worth it
Not for that type of system
what if there's 20 players fighting, each hitting each other 4 times a second that's 80 database queries per second to check if any of those player's are allied to eachother. not very good for a real time game. not to mention any other load the database might have at that time from other plugins
sorry i misunderstood?
If the player is online you cache his data
You don't run sql queries on damages, you'd never be able to do that async and cancel the damage
I'm gonna write in plain english
Step 1:
Player join,
Load his data from database and cache it on the server(instance he is now)
Step 2:
Write the plugin messaging system where it will be able to make the player become an ally through servers
Step 3:
Save player's data every time it changes, so if player1 sends an ally requests from server1 and player2 receives it on server2, you'll run a query to update the data on the database, if for some reason player2 connects to server1, you'll do step1 to him,
the data cached will be the data on the sql database, which would contain player1 as an ally
You're overcomplicating this
Ohh I get it
So in essence use plugin messaging to queue server caches for updating.
SQL query is heavy operation, don't ever run it on the main thread
I use async Dw lol
On the ally changes, yes
But you only keep the player's data loaded on the server he is at the moment
not on all instances
Yes
Thanks so much for talking through with me and now I don’t have to rewrite all my code
From my knowledge that's the best you can have without wasting 10 days writing something
And it's a system i've already tested on more than 1k player's at the same moment on a network
I hope it helped 🙂
Yeah the way to go now
hello, DisconnectEvent happens when a player leaves a server or just when he disconnects from the proxy?
is there a way to listen for the first one (or just plugin messages from each server?)?
proxy
If you want to detect when a player disconnects from one server to another or connects on first time on the network, go with ServerPreConnectEvent or ServerConnectEvent,
both mave methods of previous server which is Optional<>
hi, can anybody tell me how can i make a clickable message on velocity? in spigot I could use textComponent, I don't see such an option here
thank you! :)
hi, is is possible to add scoreboard api into velocity?
eeeeeerrr
your question makes little sense of what you're actually asking
can you add one yourself mesing around with packets? yes
is there a library for that somewhere? idk
is veloity planning to add an api for it? last I knew it was something they planned to do with as much excitement as a sandpaper dil.. nvm
both of you; scoreboards are being worked on. I just want an implementation that doesn’t have the issues the bungee impl has
and that is, both time-consuming and difficult to design test and deploy
Though its not the number one priority its also no dead-last.
Is there a similar velocity equivalent of this, or will I need to write it myself? https://www.spigotmc.org/resources/commandsync.52093/
Don’t think there is an equivalent
This is gonna be rather hard to do on velocity given commands adhere globally and not just per-player
You may be able to hack around it by using conditional brigadier predicates but in the end- I don’t see the point of this
I mean it’d be easy to do, I just don’t think it has been done, essentially it’s just sending plugin messages to the server and the server then reads the message and runs the provided command as console
It would probably be best to add velocity support to the aforementioned plugin or just ask the creator to if it’s not open sourced
I’d imagine the proxy part is rather small
Unless there’s no players on the sub servers - in which case it’d have to use sockets 🤷🏻♂️ but I don’t think that’s what it does
It does use sockets
Last commit was - 7 months ago so I’m unsure if it’s still a thing
https://github.com/A248/ExecuteEverywhereReloaded works with Velocity, but it requires a Redis database.
public ProxyServer getProxyServer() {
return proxyServer;
}```
this auto-fill getProxyServer does not work with with the below code because it is static.
```java
public static void globalUpdate() {
for (RegisteredServer server : Worldmc.getProxyServer()) {
for (Player player : server.getPlayersConnected()) {
player.sendPlayerListHeader();
}
}
}```
nobody is stopping you from using dependency injection
also, nobody is stopping you from making your own singleton if you're lazy
Cheers
I want to translate this message into English. But it returns me a message in my language (ru_RU). Is it normal?
I actually wanna remove some commands on command suggestion typing /, I see that TabCompleteEvent is only for 1.12.2 and below. What would be the right event for 1.18.1?
I mean, generally, do perms properly
if you wanna remove commands for earlier versions they need to be removed from the command structure sent to the client
but, stuff using perms properly is the proper solution to all of this
I can't, some plugins still show up commands what actually not allowed in perms and I can not replace them at the moment
PlayerAvailableCommandsEvent perhaps?
Thanks, that looks promising. I also would like to do it via perms but I see no way to remove commands like bukkit or vivaversion and other plugins without explicit permissions via perms from command suggestion.
Hi, which packet class is responsible for sending tab completion for the commands that exists?
@sacred musk whatever you’re trying to do here is a bad idea. you should rather edit the command structure with this event. But alas i can’t stop you, the name of the class on velocity is TabCompleteResponse
Thanks, I didn't even look at the messages above
you may also partially need https://jd.velocitypowered.com/3.0.0/com/velocitypowered/api/event/player/TabCompleteEvent.html for 1.12.2 and older server connections (will fire only on non-command tab completes on 1.13+)
why there is a
emoji 🤔
Only those who do not understand the awesomeness of Kotlin would dare to use it.
hello, simple question.. do I use runAsync here or the method itself will be called in a separate thread when @ subscribe is async=true?
@Subscribe
private void heavyTask(ServerConnectedEvent event, Continuation continuation) {
CompletableFuture.runAsync(() -> {
I thought it was EventTask.async?
you can do EventTask or add Continuation as a parameter from what I've read from docs
Never used continuations, so you've lost me now.
Is there any way to add custom json to a ping response?
Is it possible to get a registeredServers max player count?
RegisteredServer.ping() returns a ServerPing object containing player count information.
Completable future 😒
Basically wanted to know if it was possible with the velocity api to add custom json elements to the ping response https://wiki.vg/Server_List_Ping
No...
Well maybe, it may cause issues though
Better off using plugin messaging channels if you wanna send custom data to the client.
Does MiniMessage not work on velocity?
Caused by: java.lang.ClassNotFoundException: net.kyori.adventure.text.minimessage.MiniMessage
Thought it was on papermc repo?
No
How do I add tab completion to a SimpleCommand?
Nvm, I should have looked through docs first.
wrong channel my bad
I would recommend ACF for commands, very easy to use.
that's what i use with paper. im just a little bit iffy on implementing it with Velocity
oh. it support velocity out of box.... welp lol ill use it now
Do I have to shade minimessage to use it
Yes
Is there a method to obtaining a disconnecting players current server?
So I just use VelocityCommandManager, right and I would use ACF's BaseCommand the same way as I would with Paper?
Like disconnecting from one server and joining another? There's an event you can use to get previous server: com.velocitypowered.api.event.player.ServerConnectedEvent
Disconnecting from proxy or server
DisconnectEvent.player.server may work
use Disconnect event and player.getCurrentServer()
But DisconnectEvent can have weird behaviour
but that would only be for when the player disconnects from the proxy, you would also have to use ServerConnectedEvent to get when player switches servers. Otherwise, if possible, i would just implement what your doing into a plugin on the servers itself
Alternatively you could keep track of the players and server and then check disconnects that way.
For server
You use ServerConnectEvent
For proxy, you use DisconnectEvent
Oh yeah, just like Elmig said
Btw, @agile ore did you got the combat sys working? you asked below ^^
im working on it right now (hard to find time lol). so i got all the commands /ally <player> | /allyaccept, etc, etc running on a Velocity plugin. Then for every player that is affected by the command, i'll send a plugin message to all servers to saying hey, if this player is on your server (is cached), REFRESH THEIR PLAYER DATA! I just have to code the plugin message part of it now
Please send large files/logs to a pastebin
A sensible, modern pastebin. Share text and source code snippets with no hassle.
Why doesn't java Worldmc.getConfig().getString("Header")
return any string, and give no errors while at it?
Worldmc class:
https://paste.gg/p/anonymous/b1b5ebfd5af94e9696aaa349d854ed95
Toml
[GlobalPlayerlist]
header = "<server>\n<players>\n<player>"
footer = "footer"```
"Header" case-sensitive?
Try getString("header") and besides, this velocity-dev. Toml4j is a whole other project.
Sorry -
for (RegisteredServer server : Worldmc.getProxyServer().getAllServers()) {
String serverName = server.getServerInfo().getName();
placeholders.put("server", serverName.substring(0, 1).toUpperCase() + serverName.substring(1));
placeholders.put("players", String.valueOf(server.getPlayersConnected().size()));
for (Player player : server.getPlayersConnected()) {
placeholders.put("player", player.getUsername());
player.sendPlayerListHeader(MiniMessage.get().parse(Worldmc.getConfig().getString("header"), placeholders));
player.sendPlayerListFooter(MiniMessage.get().parse(Worldmc.getConfig().getString("footer"), placeholders));
}
}```
Besides the error has switched up on me. `java.lang.NullPointerException: Cannot invoke "String.replace(java.lang.CharSequence, java.lang.CharSequence)" because "richMessage" is null`
Error is caused by .parse(null
So the null pointer from your config didn’t change
Not sure how you’re doing config but you may need to retrieve the config section first
https://discordapp.com/channels/289587909051416579/908507886420910101/935112261431853147
in the worldmc class here
I'm inexperienced with toml, what do you mean retrieve the config section
There is almost no difference between the documented quickstart and my ver
then that's an issue with toml, not velocity
Bu, you have no config option called header
"Non-existent keys return null." Debug your file loading and dump your Toml config into a console 🤷
you have an option called header under the global playerlist section
but, not under the root of the config itself
there ya go
Thank you both.
Is there a 'postDisconnect' event?
Or a way to get server.getPlayersConnected() after the disconnect event has completed
what are you trying to do?
Ordered tab list.
just remove disconnected player from tab list
Is there a way to order tablist entries?
using teams, yes
How do I access teams with velocity?
Well, you don't
.sort ?
sort is useless, order in the tablist is handled by the client
oh
Does anyone have any advice on bridging player inventory/armour/health/hunger/enderchests/XP between servers?
if i use async MySQL to load/upload data on player join/quit I imagine that in some cases, it might to result in catastrophe.
well, I mean, if you have full control over the server switch process, yes
But, that gets more nuanced into state handling, etc
trick people use is to basically just delay a few seconds on the other end, which, blows and has its own set of issues, but, er, outside of other things like keeping stuff sync'd up over a message broker in real time, etc, glhf
yeah, im realizing this headache now. for every pro, there seems to be an equal con. would it be practical to sync the playerdata folder using the network somehow? or is that a bad idea?
no
the entire reason why using mysql creates issues there creates issues with the entire process in general
you start joining the new server before you leave the old one fully
nods head
https://github.com/WiIIiam278/HuskSync This uses Redis and i guess MySQL for persistence. Looks good! I'm going to give this a shot. too much overhead to make something from the ground up.
Then how is it possible?
I mean
you have to use teams
so, either you send the packets out yourself or use some library, or, you don't
what error does it give?
output jar is probably in proxy/build/libs
You need to grab the jar from the proxy -> build -> libs folder
^ make sure you grab the -all.jar
if there is none with that ending then it didn’t compile correctly
Hi! Is there a alternative to BungeeCord's ProxiedPlayer#getDisplayName()? Or is that Player#getUsername()?
Velocity doesn’t have a concept of “display name” (but that may change in the future)
oh ok... Well thanks anyway!
Wait.. Player.getUsername() will return the player's username, right?
yes.
is there a way to enable debug logging in the console? i'm using Logger#debug to send debug messages but i can't see them
Scoreboard api?
Coming someday, no eta tho.
❣️
https://logging.apache.org/log4j/2.x/manual/configuration.html#SystemProperties
Adding the enviroment vraible LOG4J_LEVEL with value DEBUG might be what you want.
It's maybe so we can't create an object from it, some people like to code Singleton with enum, so maybe some people like to create Utils with Enum so noone can create an instance instead of creating a final class and set a private constructor
Kind of weird but it works
there is nothing wrong with using static finals in utils classes
Yes but static method are already finals
and that's a variable?
static variables aren't final unless you add the final modifier
lol yes
i misread im on phone half the screen is hidden i didn't see it was a variable
This is how i would send a plugin message, right?
I've also never used Optional<> before. Does that look right?
That looks fine
Does GlobalTranslator in Velocity replace en_US with my default one? I'm trying to translate velocity.error.cant-connect to en_US but I'm getting a message in another language. Other languages from velocity.l10n work correctly
well, do the other translations exist in the global translator?
yes. problem occurs only when translating to en_US
before launching Velocity, I changed the default language to en_US. now everything works correctly.
Anyway, it shouldn't be like that, right? (I'm talking about the problem that I described above)
Can I use hover and click in one message when using MiniMessage?
yes
how?
add a hover tag, follow it with a click tag
Can I just post a hover tag here?
yes
thanks
How do you actually "create?" a KickResult event so I can actually do setResult() on a KickEvent?
for example KickedFromServerEvent.DisconnectPlayer.create(whatevr)
with all the relevant subclasses
Cool, cheers
is there any way to use broadcast? I don't see anything about it in MiniMessage
minimessage doesn't have a concept of broadcasting
if you need to send a message to all players, i believe ProxyServer implements Audience so you can use the sendMessage method on that
Where might I intercept this for a better message of my own?
instead of fireAndForget you can use the connect method which leave you responsible for error handling
idk if you can do anything about that from solely the plugin side
unless you had some custom proxy plugin handling a custom plugin message transfer request
Yeah I already use rabbit for lots of comms, so maybe i'll just use that to make the proxy do all server connections
so i can have better control
Intercept? Nowhere really. Possible to intercept it as the outgoing packet, but that‘s obviously not supported. Besides that, the message may be changed using the translations
👍 I'll just do the above ^
Hey all, is there a method of real time communication between a Paper plugin on a server and a Velocity plugin on a velocity server? My solution years before was UDP messaging. Thanks!
Well there is plugin messaging but that relies on an active player connection
It’s as realtime as the minecraft java protocol is I guess
Hey, thanks. A player being connected works for me. Do you possibly know of any examples that I can work from?
can I use server.SendMessage () to send a message to players on all servers?
if server is ProxyServer then yes
is there an event that fires when a server is started?
For a backend server? no
Right, you don't know when a backend server (paper in this case) has started, you can use a messaging service if you need
Can I cast ChannelMessageSource to Player?
How can I determine if a message originated from a server (velocity or paper) or player for filtering please?
is there any way to edit the locales without updating the jar?
Velocity will include it once it’s in adventure proper
To quote Kash on that:
“we plan to merge minimessage into the main adventure repository for the 4.10.0 release”
When that has happened and velocity has updated then it will be included
Not yet
ok ty!
Plugin message? Check the source instanceof
If ServerConnection then it originated from the server, if Player then it came from the player
could I edit it programmatically?
You can edit the project translations directly and compile it yourself after
But that’s pretty much the only way besides editing the jar
okay thanks
Excellent, got that, thanks 🙂 I'll take my chance and ask how to do similar in Paper in this channel?
If you’re on the server then you can’t know where the message came from
The proxy is transparent to the server;
You can probably check if the player is connected to a proxy using the bungeecord api to query something like the current server
But other than that there isn’t much you can do
Alright, thanks so much.
I was overthinking it, all players need to go through Velocity so won't be directly on the server.
You may want to encrypt your traffic on both sides or sign it if you’re concerned. Put a token in your config that’s used to do that, it’s what velocity does to do modern/bungeeguard forwarding
Will keep that in mind, thank you.
A kind of secure proxy messaging solution is also on the table for the next gen of velocity forwarding
But that’s something for the far future
Hi, I would like to know if there is a way to get access to guice injector of velocity so I can create my own objects with injector.getInstance()
How can I replace the @version@ string with gradle kts?
ContentFilterable#expand can do the trick
any sample code for brick brain? i tried to check from the all mighty google but still could not figure that out
tasks.processResources {
expand("version" to parent!!.version)
}
``` maybe this is in the wrong task
might need to add the @@?
didnt help :/
nvm i had typo
This plugin will help you to replace the version in the @Plugin
Or if you want to replace in the .json
you could use
processResources {
filesMatching(List.of("yourFile.json")) {
expand("version" to project.version)
}
}
and replace @version@ with ${version}
velocity has any native configuration loader?
configurate 3
I can't find in the docs
you'll want to look at https://github.com/SpongePowered/configurate (their wiki/javadoc), although note that velocity currently bundles version 3.7.3 and not the latest of 4.x
Right, thanks!
Hey guys, how can I dispatch a command?
I saw I could use server.getConsoleCommandSource() to get the console command source
Thank you, this is exactly what I needed.
I read this: https://velocitypowered.com/wiki/developers/command-api/ and I'm not sure to understand how to work with args using the first syntax
LiteralCommandNode<CommandSource> helloNode = LiteralArgumentBuilder
.<CommandSource>literal("test")
.executes(context -> {
Component message = Component.text("Hello World", NamedTextColor.AQUA);
context.getSource().sendMessage(message);
return 1; // indicates success
})
.build();
Here is what I did, I'm not sure of what I am doing: https://gist.github.com/Th0rgal/7e688a2da68af14fb8abbcb49bbced2a
you can use a literalcommandnode for each subcommand and add it as a child of the main node
instead of having to use a switch
^ you generally want a (brigadier.)Command lambda function per executable node in the tree. Let Brigadier do the parsing logic for you
Oh that’s great
How do i get the version the player is? in my plugin i have some messages and i want to know if the player is in a version after 1.16 to use rgb colors in my messages, how can i get to know the version of the player?
er, check if the API exposes the protocol version on the player?
Player has a method getProtocolVersion inherited by InboundConnection
yes
Does Velocity expose a plugin messaging channel to the backend servers similar to this?
https://www.spigotmc.org/wiki/bukkit-bungee-plugin-messaging-channel/
The home of Spigot a high performance, no lag customized CraftBukkit Minecraft server API, and BungeeCord, the cloud server proxy.
Velocity natively supports the BungeeCord messaging protocol, so any BungeeCord plugin messages you would normally send from your backend to Bungee will work the same with Velocity
sweet, okay. thanks!!
player.playSound(Sound.sound(Key.key("minecraft:entity.enderdragon.death"), Sound.Source.MASTER, 1.0f, 1.0f), Sound.Emitter.self());
Any clue why this doesn't work?
.
a
This happens all too frequently. A solution ought ot be devised
audiences fail silently by design
Yes, I'm well aware
this is not a named sound, Velocity does not support sending sound events that aren't named due to the fact it would need a way of translating this key into the int(?) id used in the packet that plays this sound