#help-development

1 messages · Page 1333 of 1

wet breach
#

they could have just done a profile dump as well

pine forge
#

i wanna get remote going though

wet breach
#

why?

pine forge
#

because im always using my remote server so others can join and it can stay running

#

theres my configuration

#

im not gonna start up a local server every time i want to debug

wet breach
#

don't need to

#

a profile dump can be ran remotely

pine forge
#

whats that

#

profile dump

#

okay its liostening now

#

i see in the console

#

still timeout connection its my tunnel then

wet breach
#

Here is a tool that should help you in whatever it is you are wanting

pine forge
#

never works

wet breach
pine forge
wet breach
#

it dumps whatever the server was doing into a file

#

you can play them back sort of

#

or just look to see what was or was not executing

pine forge
wet breach
#

could just click the link at top for the documentation

pine forge
#

okay got remote debug working

#

so it does not look like intellij can map the codepoints to my local source code

#

i cant see what code its executing

wet breach
#

well we have no idea what you are looking at

pine forge
#

not all of it atleast

#

i set a breakpoint at dispatchCommand

wet breach
#

well some of the code steps should go through your plugin if you are executing a command

#

which won't be in the server source

#

since well its your plugin

thorn isle
#

he's trying to get the server to run an arbitrary command and capture the command output by passing it a spoofed command sender, which is supposed to log anything it receives

#

so he needs to see what's happening inside the nms/cratbukkit parts of the command execution, specifically whether either of them unwraps the commandsender somehow

wet breach
#

I would imagine it would need to?

thorn isle
#

not for bukkit commands i don't think

#

vanilla commands get the unwrapped nms command sender

#

either way you'll probably want paper userdev's mojang-mapped sources, and ideally those for whatever build your server is on, so all the line numbers and other things align

wet breach
#

yeah that would be handy

#

especially if you are new at doing this lol

thorn isle
#

the remote server doesn't exactly send your debugger the classes you're stepping through, just the class name and line numbers, so shit goes whack when you're looking at some mangled decompiled class in your ide

thorn isle
#

basically instead of a specific .jar artifact , you depend on paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT"), which adds a bunch of jar artifacts, one of them being the mojang-mapped server and another its sources

#

the downside is that it requires gradle which i would like fucking use apache ant over, but it is what it is

pine forge
#

i use gradle so it sfine

thorn isle
#

or if you're building against/using spigot, you do the whatever buildtools nonsense, but i don't remember how that works

pine forge
#

do they not have 1.12.2?

thorn isle
#

i doubt it

pine forge
#

okay

#

whatever

slender elbow
#

mojang mappings did not exist back then

pine forge
#

should be the version of my minecraft server anyways

#

right

thorn isle
#

glhf basically

slender elbow
#

if you are compiling against and running paper, there is api for a faux feedback CommandSender, but that also did not exist in 1.12 lol

thorn isle
#

oh neat, that's new to me

#

what's it called

#

i might have to do this again later and i'd rather not nms into command blocks a second time

slender elbow
#

Bukkit/Server#createCommandSender i believe

thorn isle
#

neat

pine forge
#

guess i need to debug

#

ah i dont think paper dev thing works with my 1.12.2 setup

#

io.papermc.paperweight.PaperweightException: Exception executing remapMinecraft

#

org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':1.12.2-spigot:paperweightUserdevSetup'.
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:135)
at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:288)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:133)
at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:121)
at org.gradle.api.internal.tasks.execution.ProblemsTaskPathTrackingTaskExecuter.execute(ProblemsTaskPathTrackingTaskExecuter.java:41)
at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)

#

i am doing this though
paperweight.paperDevBundle("1.21.10-R0.1-SNAPSHOT")

lilac dagger
#

Remapping goes brr

#

Spigot doesn't have runtime remapping

#

But also paper shouldn't have runtime remapping on 1.12

vagrant stratus
#

Gotta love using older versions of things 😎

pine forge
#

isnt there a similar thing for just craftbukkit stuff

young knoll
#

Crazy how we went from no mappings in 1.12 to no obfuscation in 26.1

dreamy glade
#

Do most players prefer to play the game with or without the "Force Unicode Font" set to true?

slender elbow
#

i doubt most players even know that setting exists

chrome beacon
#

^

thorn isle
#

i do see a decent number of players using fonts from their resourcepacks, but i don't remember seeing many using the unicode font

#

i'd say it's about as popular as using pirate speak as your language

slender elbow
#

lmao

young knoll
#

I know it exists!

#

I don't really know what forcing unicode font does though

slender elbow
#

wanna find out?

dreamy glade
young knoll
#

Does it just disable the default font and make everything use unifont

chrome beacon
#

yeah

slender elbow
#

one could say that it forces unicode font, even

young knoll
#

i'll unicode your font

slender elbow
#

go ahead

lilac dagger
ancient plank
lilac dagger
#

I can't wait to have just one nms module going forward

#

Hopefully it works for future versions too

chrome beacon
#

I mean breaking changes will still happen

lilac dagger
#

That's sad

dreamy glade
young knoll
#

MD on his way to continue relocating Craftbukkit per version

lilac dagger
#

It'd be better if he will leave it up to the devs to update and check

#

Ewch new drop i'll add it to a stable version and then if something is untested the players will know

young knoll
#

Mojang does like refactoring things

lilac dagger
#

True but think of all the saved nms modules

thorn isle
#

ever since paper rolled out mojang mapped nms plugins and server, maintaining nms plugins has been a breeze

#

most of the time they work without building, build without changes, or need only some minor refactorings

#

i find that the bukkit api breaks more often than nms now, with sound names and potion effects and shit being changed

distant cosmos
#

when should I depend on netty for my plugins?

lilac dagger
#

When you need to sniff or modify packets

pine forge
#

okay i will stick with console logging, this does not seem to work

#

looking at the bukkit code, consolecommandsender is converted to getServer().createCommandSourceStack with no reference to the sender methods

#

and my blockcommandsender cant be cast to craftblockcommandsender

#

id have to use reflection or something

#

theres also ProxiedCommandSender which is cast to ProxiedNativeCommandSender though which i cant create

lilac dagger
#

Not sure if it's supposed to use that way

#

There's player precommand event

#

I'm not sure what you wanna do tho

pine forge
#

i wanna get command output

lilac dagger
#

Yeah playerprecommand event

#

You get all the args and label

#

Ah console

#

Hmm

pine forge
#

its weird though that consolecommandsender doesnt even seem to capture plugincommands

lilac dagger
#

It depends on server fork you're using

#

Some versions of paper for example didn't hook into simplecommandmap anymore

#

Later they added a delegate map for registering commands into brigadier anyway

pine forge
#

ah yes

#

okay

#

that makes sense

#
return new Handler() {
    @Override
    public void publish(LogRecord record) {
        if(record == null) return;
        String message = record.getMessage();
        if(message == null || message.isEmpty()) return;

        if(output.length() > 0) output.append('\n');
        output.append(message);
    }

    @Override
    public void flush() {}

    @Override
    public void close() {}
};
``` using this from JUL now
lilac dagger
#

Yeah probability the best

#

Capture the logs

pine forge
#

worked well before

#

Bukkit.getLogger().addHandler(captureHandler);
used log4j before though i believe

#

this is new

#

lets test

lilac dagger
#

Might be cool to have a consoleprecommand event

#

Altho it might be misused

pine forge
#

might be cool to just fake a command sender like in newer versions

#

i think thats the best way

lilac dagger
#

You might be able to do it still on even older versions

#

I think paper did the change since 1.20.6 to 1.21.4

#

Also did you check if brigadier has logging options?

#

Might be less hacky than fake console senders

pine forge
#

you mean with reflection?

#

well i want to support from 1.12+

pine forge
#

that would work

lilac dagger
#

With Nms modules

#

Ah 1.12 didn't had brigadier tho

#

Or at least I know of

pine forge
#

okay the handler doesnt capture anything either

#

lets do log4j again

#

guess this was a waste of time

thorn isle
#

did you try rcon yet

pine forge
#

wdym

thorn isle
#

that's existed since like beta or something

pine forge
#

ah

#

you mean connecting with rcon

thorn isle
#

myes

pine forge
#

well not all servers have that enabled

#

and its like a network connection

#

slow

#

lots of logic

thorn isle
#

it's a completablefuture anyway

pine forge
#

doesnt mean i wouldnt prefer a quick response

thorn isle
#

it's going to be quick on the scope of like under half a millisecond or something since it's a loopback connection

pine forge
#

okay hmm

#

might make sense

#

ill try that

#

ill fall back to console capture if rcon is disabled

thorn isle
#

yeah that seems reasonable enough

pine forge
#

rcon is not enabled by default it looks like right?

thorn isle
#

yeah since it allows anyone with the password and access to the port to run any commands they want

#

not something you want active by default

pine forge
#

okay well

#

99% of server swont have it enabled then

thorn isle
#

servers can enable it

pine forge
#

but why would they

thorn isle
#

because your plugin installation guide tells them to

#

just like some plugin installation guides tell people to set up a mysql database

pine forge
#

well my server actually had it enabled

#

but no i dont think its important enough to give people extra hustle in installation guide

#

lots of people dont even know what serve.rproperties is

pine forge
#

so rcon just cuts off command responses?

#

i dont get the full one

thorn isle
#

there's like a 4k character limit for the response

pine forge
#

not sure thats 4k: Lianecx has the following entity data: {Slot: 0b, id: "minecraft:white_shulker_box", count: 1, components: {"minecraft:block_entity_data": {id: "minecraft:shulker_box", Items...

#

lets see if its a diff issue

#

The Minecraft server can fragment responses across multiple packets. There's no simple way to know when the last response packet has been received; approaches include:

#

stupid

#

wtf

#

Wait until we receive a packet with a payload length < 4096 (not 100% reliable!)
Wait for n seconds
Send two command packets; the second command triggers a response from the server with the same Request ID, and from this we know that we've already received the full response to the first command.
The second packet should use a command that will not produce fragmented output
An alternative is for the second C->S packet to use an invalid type (say, 200); the server will respond with a 'Command response' packet with its payload set to 'Unknown request c8'. (200 in hexadecimal)

thorn isle
#

good shit

pine forge
#

still seems to cutoff

#

idk

#

mean it returns these ... itself

#

i dont think i have that in my code

quaint basin
#

How does DonutsMP allow players to be teleported to coordinates like 100,000? Do they generate the chunks in real time because they have a powerful processor?

vagrant stratus
#

magic

#

but also good RTP plugins

#

but also magic

quaint basin
#

I don't know if I should put the resource world on other servers; I have the nether, end and overworld

vagrant stratus
#

They're likely just using an optimized teleportation plugin, don't really need a high end CPU just to teleport somewhere lol

quaint basin
#

The problem isn't teleportation itself, but rather the loading of chunks from various players

vagrant stratus
#

Also not difficult. Not much different than regularly playing

#

Doesn't really take much to pre-load chunks either

quaint basin
quaint basin
thorn isle
#

you can pregenerate them on the fly

vagrant stratus
#

^

thorn isle
#

your cpu won't always be at 100% load; you use the idle time to pregenerate some pre-chosen rtp spots as you're able

#

and then teleport the players to those spots on demand

#

this is what i was talking about when you first asked help here, with your rtp plugin

#

i remember spending several hours explaining the concept of a ring buffer

quaint basin
vagrant stratus
#

or just generate a world and generate the "starting" chunks. Minecraft handles everything else via the world seed itself

#

You don't have to load in the entire world lol

#

Realistically, all you'd have to do is load in the same amount of chunks as the server normally would and you're fine

#

5x5 chunk area

#

now if you want to pre-load the entire world you're kinda fucked regardless

vagrant stratus
#

I don't think you have almost 7 TB lying around to pre-gen an entire 100k * 100k world 🙂

umbral ridge
#

meh

#

who really explores the whole world

#

has to have a mental thing going on

vagrant stratus
#

exactly. pre-gen 5x5 or even 10x10 and let the server generate the rest as needed lol

umbral ridge
#

or 50k x 50k enough for 20 people

#

now that there's so many stuff cramped up below and above

thorn isle
#

spoof the player coordinates

#

keep everyone in a 1000x1000 box but spoof random coordinates in +/-3,000,000 every time they rtp

umbral ridge
#

yes

#

and add a click button that allows players to teleport to them for free

thorn isle
#

iirc there is or was a "coords obfuscator" plugin some rpg/hardcore servers used to make it harder to cheese with f3

#

you'd actually have to use maps and learn landmarks

umbral ridge
#

hmmm

#

is that even possible to do?

#

to hide F3 coords

thorn isle
#

not hide, but they filtered all packets to change the coordinates with random numbers

quaint basin
thorn isle
#

or like apply a random offset to them, which changes any time you teleport

vagrant stratus
#

speaking of chunk stuff. Debating on if I wanna try to re-create the chunking system in C++
Making a 2D ASCII game & it'd be an interesting-ish challenge

umbral ridge
#

what i wanna do soon is my own dynmap

#

that will be interesting

#

and a good challenge

thorn isle
#

you know i've had this custom dynamic map idea rattling in my mind of thoughts for a while

#

earth map servers all mercator projection, which is shit and bad and makes greenland larger than the rest of the world combined

vagrant stratus
thorn isle
thorn isle
#

which essentially splits the world into distinct triangles and other shapes

vagrant stratus
umbral ridge
#

I already see how im gonna do it

thorn isle
#

it'd have a "portal boundary" in the oceans where you'd get teleported from one shape to the other

umbral ridge
#

i would use a grid system

#

chunks and blocks

thorn isle
#

and for the dynamic map, you'd map it onto an actual sphere

quaint basin
#

To get a biome without loading the chunk, I just need to do world.getBiome(x, y, z).getKey().getKey(), right? I saw that ppl suggested me using world#getProvider to do this, but I don't think it's necessary

thorn isle
#

i don't know, check the nms

#

i remember there are calls that generate/load the chunk and calls that don't

quaint basin
thorn isle
#

depends on how fast you can load a chunk

quaint basin
#

In this case, if this loads the chunk, it also generates

#

Because the coordinates range randomly from -1 million to 1 million

#

I think it's impossible to generate and load in that time

young knoll
#

It makes a call to WorldGenLevel#getNoiseBiome

#

So it probably doesn’t load the chunk

thorn isle
#

yeah that won't load it

quaint basin
#

If I use the ring buffer for my RTP plugin, after checking the biome, should I generate/load the chunk to check the valid Y coordinate for that location if there is one; if there isn't, should I not add it to the buffer? Or should the buffer only contain locations that were valid in the first step?

#

My concern is that if I check everything before adding the buffer and the buffer size is 100, then every time the plugin starts it will generate 100 chunks (in the worst case, which is mainly the case since the locations range from -1 million to 1 million). To solve this, I would have to save the buffer to disk, but I don't think that's a good solution

sly topaz
#

RTP plugins don't need to be complicated

thorn isle
#

the buffer should only contain locations that you can immediately teleport players to; the surrounding chunks should be pregenerated, and the location should be a valid teleport target

thorn isle
#

the point of this is to do the work ahead of time, if you just put a bunch of points in a buffer without checking if they're valid, you might as well pick the points on the run and get rid of the buffer

sly topaz
quaint basin
quaint basin
sly topaz
#

why would saving some locations end up with high disk usage

thorn isle
quaint basin
# sly topaz what high disk usage

If my buffer size is 100, I'll be generating huge chunks; if I don't save the locations of this buffer when the plugin shuts down, I'll have to find another 100 valid positions and generate 100 chunks and their corresponding chunks, and those positions shouldn't even be used anytime soon

#

That's why I think I should save the buffer positions when the plugin shuts down

thorn isle
#

you should, we agree

#

it sounded like you were saying you didn't want to save the buffer positions because they'd use a lot of disk space

#

but perhaps i misread

quaint basin
#

What I meant was that not saving would use high disk usage

sly topaz
thorn isle
#

personally i don't bother saving them, i just generate 50ish fresh ones every restart, but i could see how that'd add up over time

quaint basin
sly topaz
#

I was tempted at some point to make a random-location based generator for chunky

quaint basin
#

But I think that approach isn't even advantageous if the world is small; if it's small, you can just pre-generate the entire world

sly topaz
#

that way you'd generate just huge spots around random safe locations rather than in a radius, but that'd only really work for some gamemodes like UHC

quaint basin
#

uhc for me is like a 100x100 map

thorn isle
sly topaz
thorn isle
#

yeah sounds about right

#

pregenerating past that will still help when players start exploring around, but there will be some chunks you pregenerate that won't be visited, going to "waste"

quaint basin
#

Perhaps in this case it would be better to deal with the average case and not the worst-case scenario idk

thorn isle
#

depends on what your maximum view distance is i guess

quaint basin
#

yea

thorn isle
#

generating up to i'd say 10 chunks radius is sensible i think, but 32 is probably overkill

quaint basin
#

I don't know how many chunks a survival server should have, but that's something I'll have to research in the future

#

maybe 8-10

#

So maybe if this value be the surrounding chunks amount, I don't think it will take up much disk space

vagrant stratus
#

Managed to get the ASCII world & chunk stuff figured out.
Don't necessarily like it though kekw

#

It'd definitely be better w/ color, but still lol

thorn isle
#

but can you build a fortress in it for your dwarves

vagrant stratus
#

I mean, I can implement that functionality lol

I need to improve world-gen first though

young knoll
#

: is my favourite block

#

also that island on the right looks like a doggy

vagrant stratus
ancient plank
vagrant stratus
#

I did slightly improve the world-gen. At least as okay it can get w/ google and ASCII lol

vagrant stratus
young knoll
#

T for Tand

vagrant stratus
#

I know reading is hard, but you can do better than that

ancient plank
#

fkin tand lmao

#

I love it

vagrant stratus
#

dropped that project for now, currently waiting for something else to run though lol

ancient plank
#

im sitting here thinking about stuff

#

like

#

do I want to actually do software engineering full time

vagrant stratus
#

waiting on custom tooling to finish up so I can test stuff lmfai

ancient plank
#

well if I don't, what else do I want to do

vagrant stratus
#

become a Shepard

#

oh ran out of heap space before that finished KEKW

mortal vortex
#

How do you guys plan things? (like SDLCs)

#

I wanna rewrite a plugin, from the ground up, and get an idea of everything it needs before i start.

worldly ingot
#

I assure you a total of zero people in this Discord server are using any form of SDLCs for their plugins lol

vagrant stratus
#

^

#

I personally just update until the code base is a PITA or has enough technical debt.
If it's a private project I'll even go as far as scrapping the github history & starting entirely from scratch, using the old version as a reference for a from the ground up rewrite

ancient plank
#

I took a project management course last semester and used the knowledge from it to design a plugin rewrite

#

did not use any of the tehcniques I learned other than general skills I picked up during it to better organize my thoughts

vagrant stratus
#

lol

ancient plank
#

for minecraft pplugins it's just not necessary

vagrant stratus
#

^ PM is more for group projects or companies

ancient plank
#

don't get me wrong that class was VERY USEFUL 10/10 would recommend

#

I learned a lot of really good things in it, but it's not a necessary skill to have when working on something like a minecraft plugin

vagrant stratus
#

I guess if you want the experience, but it's still massively overkill

ancient plank
#

I recommend listing what you currently have, what you want to have, why you want those things, and doing iterations over it to improve the explanations and features

vagrant stratus
ancient plank
#

written fresh out of that class

#

wanted some hands on experience bc I was fkin struggling to understand lmao

vagrant stratus
#

note, probably over kill as fuck

ancient plank
#

for sure

#

helped me a lot tho

vagrant stratus
#

oh 100% but it's not really required

ancient plank
#

I tend to get lost in the sauce when making new stuff

vagrant stratus
#

Similar to doing game design docs and stuff solo

ancient plank
#

mhm

vagrant stratus
#

not required, but useful lol

ancient plank
#

I wrote a cute lil pipeline to explain the stages of damage calculation

vagrant stratus
#

on another note, praying the changes I've made don't take the entire Java heap again KEKW

#

Trying to index a few thousand things in memory atm

ancient plank
#

oof

ancient plank
#

we had a server crash and I downloaded the heap dump forgetting that you need like 2x memory to read a heap dump

vagrant stratus
# ancient plank oof

I don't need too, but it'd sure as hell make one of the custom tools I'm working on easier lmfao

ancient plank
#

I only have 32GB of ram that thing was like 20GB

vagrant stratus
#

been expanding my reverse engineering & analysis program all day lmao

ancient plank
#

:)

mortal vortex
quaint basin
calm dust
#

hey, i`ve downloaded quests plugin, do i need to setting my own quest or they alredy have the default quest

thorn isle
mortal vortex
slender elbow
#

or what

pliant topaz
# mortal vortex How do you guys plan things? (like SDLCs)

At most some basic notes for a broad overview, maybe a few specific implementation details, but most of the improvements happen because I now know what system needs what and can improve it from the ground up (often resulting fully recoding the entire class from scratch as its the easiest in my opinion)

thorn isle
#

my grandfather would, when presented with any large or overwhelming problem, always say "start from the corner", referring to the side of a farm field

#

in similar fashion i just sit down and start hammering at it in whatever class seems like a decent entry point, writing //TODO's for things as i go

slender elbow
#

i always start with the SpringAbstractedMongoServiceBuilderWebProvider.Factory

#

and then get off all day long thinking about best practices and clean code and ideal paradigms for java

quaint basin
thorn isle
#

I'm saying listen to chunk load event and if the chunk contains a location that is in the buffer, remove it from the buffer

quaint basin
#

oh good

thorn isle
#

This means it is impossible for points in the buffer to become invalid

quaint basin
#

But verifying whether a location is contained within a chunk, I don't know if that's an easy task

thorn isle
#

It is very simple

#

Build a chunk coordinate->buffer location map when you populate the buffer

#

And query it on chunk load

quaint basin
#

oh good

#

what's the site with minecraft source code?

vagrant stratus
#

There isn't one

quaint basin
vagrant stratus
#

nope! legally we're not allowed to host the code

#

gotta use either buildtools or paper's thing to get acess to it

quaint basin
#

Paper members send this website on chats

vagrant stratus
#

That's not source, but documentation

quaint basin
#

There is literally a website online with the Minecraft code

#

I think Minecraft now offers the code

#

i just dont remember the url

vagrant stratus
#

You'd still need a decompiler, as they only provide jars

#

de-obfuscated versions of MC don't even go back to whatever version you're usnig

#

They're only 1.21.11+ or whatever mojang said

quaint basin
quaint basin
#

That's not what I'm looking for. I'll see if anyone knows

vagrant stratus
#

that's the only way you're getting it afaik

quaint basin
vagrant stratus
#

yea, uses the same jar I just sent lol

slender elbow
#

if only the huge text block that reads "How It Works" that shows up immediately when you open the website explained how the site does indeed not host and serve the code but rather the binary jar is downloaded and decompiled locally

#

oh well, i guess the world shall never know

vagrant stratus
quaint basin
#

Is it possible to generate a chunk without loading it?

thorn isle
#

No, generation happens in memory

#

Generation also requires accessing adjacent chunks, so even if you wrote your own chunk generator that writes it to disk directly, you'd still need the adjacent chunks

tender shard
quaint basin
quaint basin
#

How many surrounding chunks are needed to generate a chunk?

tender shard
thorn isle
#

the server will load the adjacent chunks at the appropriate chunk stage/level transparently

#

it can't not work

quaint basin
tender shard
#

well okay that's a bit large for pregen lol

quaint basin
thorn isle
#

for more about chunk stages, read the wiki or this thread where i explain it to someone else

thorn isle
#

they will be at whatever stage is necessary to get the information for the generating chunk

#

generally 1 stage lower than the generating chunk

quaint basin
#

i see

thorn isle
#

also this

#

you can essentially think of it like building a pyramid; you can't just start from the center and pile it 100 stone blocks tall, because it will fall; it needs to be surrounded by adjacent columns of stone blocks, which in turn have to be supported by adjacent stone blocks, forming a step-pyramid shape

#

this is also why it is much cheaper to look for valid teleport positions in nearby chunks even if you can't find it in the chunk you randomly chosen; it's cheaper to make an already-tall pyramid slightly larger, than build a whole new pyramid somewhere else

quaint basin
#

i see

quaint basin
thorn isle
#

depends on the way how it fails; if it fails because it's water, it's very likely that you're in a river or a lake and many surrounding positions will also fail the same condition

quaint basin
#

The issue is that sometimes this might not be advantageous because the plugin might simply try multiple times (and this happens because it tries a nearby location, for example, a player building a huge trap), which ends up not being advantageous to simply "build the pyramid" from scratch

thorn isle
#

if it fails because it's e.g. covered by logs, it's probably a tree and a nearby position is still likely to be valid

quaint basin
thorn isle
#

it's much cheaper to find a valid position in a loaded chunk than it is to generate the chunk, so you don't want to spend 100ms generating a chunk and then discard it in 1ms

quaint basin
thorn isle
#

well, logs aren't leaves

#

you can end up being unlucky and picking the actual tree trunk for your location

#

or a cactus, or any number of other things

quaint basin
#

but getHighestBlock will return the air block

#

What can happen is that it spawns on top of a cactus, for example

thorn isle
#

no, it will return the highest non-colliding block except for leaves

#

i.e. it could be inside the leaves

#

on top of the tree trunk in the tree

#

or on top of a cactus (you don't want to rtp the player there either)

#

my point is that you will have this kind of "spurious" failure of the search conditions; just search a few nearby locations if you do

#

but for other types of failures that are indicative that the whole area might be invalid, it's better to pick a whole new chunk

quaint basin
#

Because the tallest non-colliding block is usually the tallest possible Y-coordinate

thorn isle
#

HeightMap.MOTION_BLOCKING_NO_LEAVES indicates a heightmap that accounts for all colliding blocks except leaves

#

meaning it won't see leaves

#

meaning if you have a block of grass and 2 blocks of leaves above it, it will return the bottom-most of those leaves blocks; a spot inside the leaves

#

it can also return the block above the tree trunk of a tree, which will also be occupied by leaves

quaint basin
#

Yes, I agree, but I disagree with the part where you said that getHighestBlock returns the highest non-collidable block

thorn isle
#

it returns the block above the highest collidable block that isn't leaves

#

which itself could be leaves

quaint basin
#

In this case, I need to check that the block chosen by getHighestBlock is not cactus, and that the two blocks above are not water, lava, leaves, etc

thorn isle
#

myes

quaint basin
#

actually, an air check is basically for the two blocks above

#

no need check all type of blocks for the two blocks

thorn isle
#

i'd check for air and some acceptable noncolliding blocks, like tallgrass, flowers, and ferns

quaint basin
#

i see

thorn isle
#

since quite many positions will be occupied by those rather than air

quaint basin
#

If you want to send me this check, I'm accepting xd

#

It's hardwork because I need to see all types of blocks

thorn isle
#

just eyeball at Material or go looking around plains in f3 and put everything you see that doesn't kill you into a switch block

#

or maybe material tags

young knoll
#

Isn’t there an isPassable

thorn isle
#

there is, but that might not be right

#

e.g. i think sweet berries might be passable despite not being nice to spawn on top of

quaint basin
#

i see

young knoll
#

You’d probably have to exclude a few

#

But that’s easier than explicitly including a bunch

thorn isle
#

i don't know, you can try it; using isPassable and a smaller manual blacklist would probably work as well

quaint basin
#

Why on earth are strawberries considered passable and flowers not?

thorn isle
#

flowers should be passable since they don't have a collision shape

quaint basin
#

oh

#

How does getChunkAsync work? When does the paper decide: Okay, this tick I'm going to load these chunks that were requested ticks ago?

thorn isle
#

there's a queue

quaint basin
#

I'm asking this because I'm using this method to load adjacent chunks from my buffer, and I never see the paper generating the adjacent chunks

thorn isle
#

well you don't see them, that's what transparent means

#

the server generates them up to whatever stage they need to be, but in bukkit, only the full stage is considered to be generated or loaded

quaint basin
thorn isle
#

getChunkAt will push the chunk to the beginning of the queue, to be loaded "as fast as possible"

quaint basin
#

oh

#

Has it always been like this ?

thorn isle
#

which generally is not good because now the server is putting everything else it's doing to the side just to generate the chunk

#

this? as in there existing chunk generation stages? yes

quaint basin
#

I asked if getChunkAt always adds the chunks to be loaded as quickly as possible and not simply load them

#

In that case, I asked if it had always been this way

tender shard
#

getChunkAt loads it instantly, doesn't it?

thorn isle
#

this kind of "sync" loading has always existed, and was the only way to load chunks before paper made chunk loading asynchronous; there didn't used to be a queue at all, calling the method would simply synchronously get the chunk

quaint basin
tender shard
#

if it does not return a CompletableFuture, but a Chunk object, then it's a blocking call

thorn isle
#

after asynchronous chunk generation/loading was added, this was replaced with a queue that the chunk worker threads watch and fulfill the requests in it as they're able

#

i'm fairly sure these days calling getChunkAt still uses those async threads (the generation doesn't happen on the main thread) but the main thread will block for it, stopping the game tick loop until it's done

young knoll
#

Isn’t that how it’s always worked

#

Vanilla use threads for worldgen too

thorn isle
#

as of like 1.13 or something

#

but back when it was all a single thread, there was no queue or chunk threads

young knoll
#

Yes

#

That’s like 50 years ago

thorn isle
#

myes

quaint basin
#

Since in my view, getChunkAsync is asynchronous, it doesn't make sense for the game's tick loop to pause and wait for the chunk to be generated

thorn isle
#

it doesn't care about ticks, it just puts in the queue and the chunk threads will get to it when they're able to; no thread will block for it as the result is a future

#

getChunkAt does the same thing, but it puts the chunk at the front of the queue and then .joins() the future, which will block the calling thread (which is going to be the main thread)

#

either way, you basically never ever want to use getChunkAt

quaint basin
#

i see

thorn isle
#

the only case where you would use it is if everything will explode if you don't get the chunk right now instantly

tender shard
thorn isle
#

perhaps

quaint basin
thorn isle
#

i'd still do it with the async overload if at all possible, but yeah, it's sort of acceptable in onEnable and onDisable

thorn isle
#

i have no idea what this code is supposed to do

#

it looks like you're picking a random location, and then pushing it into the buffer viewDistance*viewDistance times?

tender shard
thorn isle
#

yeah, accessing the heightmap of a chunk will load/generate the chunk

tender shard
#

the check for isPrimaryThread is also not needed, the javadocs say that the future returned by getChunkAtAsync runs on main thread

thorn isle
#

sort of maybe

quaint basin
thorn isle
quaint basin
tender shard
thorn isle
quaint basin
#

I just need to add an if-check on for: if dx == 0 and dz == 0 then continue the for loop

tender shard
#

are you doing a random TP plugin or what are you even doing?

thorn isle
#

why not add the location into the buffer first and then loop over the nearby chunks to generate them?

quaint basin
#

This way, I prevent the player from using /rtp and getting a position in the buffer where nearby chunks haven't yet been generated

thorn isle
#

there's no guarantee the that the first future completes after/before all the chunks are generated

#

since it's a queue, it's liable to complete before all the chunks are generated

#

if you want to make sure the chunks are generated, put all the futures in a list and then do CompletableFuture.allOf on them, and only then put the location in the buffer

quaint basin
# quaint basin This way, I prevent the player from using /rtp and getting a position in the buf...

The problem with this is that if someone enters this chunk before the push and adds blocks, and by chance places a block in a "valid" position that would be added to the buffer, that position will be in the buffer and is no longer valid, and the chunkloadevent will not be called after being added to the buffer. To solve this, simply implement the logic of chunkMap.computeIfAbsent... inside the push, before the for loop inside the RingBufferRunnable, so that if the chunkloadevent is called for a chunk that is in this map but not in the buffer, then add it to a cache structure so that when actually adding it to the buffer (inside for-loop on RingBufferRunnable), check if it is not in this cache structure and if it is, remove it from the chunkMap.

https://pastes.dev/fDzI4Ndgdl
https://pastes.dev/tVxeA9dl5t

thorn isle
#

you're overthinking this a bit

quaint basin
thorn isle
#

since the futures are guaranteed to be completed by the main thread i suppose the counter does work, but allOf is still the better approach

#

just be aware that this most likely won't work in any other context where you'd use futures

quaint basin
#

I don't understand why you think the counter shouldn't work xd

thorn isle
#

it happens to work in this case because all the futures are guaranteed to work on the same thread

quaint basin
#

Yes, of course; I know this works because what completes the CF is the main thread

thorn isle
#

without such a guarantee, you since you're not incrementing it atomically, it's liable to never reach totalIterations

#

either way, allOf is a much easier structure to reason about and also allows you to e.g. capture thrown exceptions in a cleaner way

quaint basin
#

I'm not using atomicinteger because I only increment/read it in the main thread

quaint basin
thorn isle
#

it's a future that completes once all of the passed futures are complete

#

so if you give it all the chunk futures, you have a "all chunks are done" future

quaint basin
#

this method is powerful

thorn isle
#

yeah that's about right

quaint basin
thorn isle
#

also remember to always either .join() or .exceptionally() on any future you run any code in

#

otherwise, you will not see any exceptions that occur

quaint basin
#

I don't usually add unchecked exceptions in a CF, but it seems like that's bad practice then

thorn isle
#

no, that exception also gets swallowed

#

you need to take the exception and log it/handle it

#

the thing with unchecked exceptions is that you don't "add" them; they just happen, unexpectedly

#

e.g. suppose there's a CME on ringBuffer.push(location), or a NPE on location.setY; you will never see them unless you handle the exceptions

#

it's easy to forget about this because java usually has very reliable exception handling, but with futures, you must always remember to add an exception handler yourself, because java will not log/handle them for you

quaint basin
#

i see

#

i'm doing this way now:

.exceptionally(e -> {
logger.log(Level.SEVERE, "Exception loading chunks", e);
return null;
});

thorn isle
#

yeah, that's how you'll want to do it

quaint basin
#

I don't really like this because, for example, if I have an exceptionally in a service, every time I use the service, I have to add another exceptionally block

thorn isle
#

yes, this is the downside of futures

quaint basin
#

It ends up being just exceptionally nested blocks

thorn isle
#

well you generally don't need to nest them because of the way how they propagate throughout futures

#

but yes, it's still ass and very easy to neglect to put an exceptionally where you need it; it isn't always clear where you should or should not put it, either

#

this is why i personally prefer virtual threads nowadays

thorn isle
#

as long as you .join or .thenX on a future, you don't need to .exceptionally on it; in case of join, the exception is automatically rethrown, and in case of thenX, that responsibility gets passed to the resulting future

quaint basin
# thorn isle as long as you .join or .thenX on a future, you don't need to .exceptionally on ...

My point is:

Let's say I have a service that does CompletableFuture.thenAcceptAsync(code block...).exceptionally(e -> return new QueryErrorResult());

Then, if I use the service's method, upon obtaining this future, and do something like thenAccept again, this thenAccept will be called regardless of whether an exception occurred or not. Therefore, whoever calls the service's future and uses thenAccept should call the exceptionally block after that right?

#

Sometimes the code inside the second thenaccept block is simple logic that you're sure will never cause an exception, but it would be considered bad practice not to add the exceptionally block ig

thorn isle
#

if it's a service/api method that returns a future, you don't necessarily have to do .exceptionally on it; you can decide that doing that is the caller's responsibility

#

otherwise these .exceptionally's will proliferate endlessly and your code will just be exception handlers

#

this is what i meant by "it's not always clear whether you should or should not put it, either"

#

either way, yes, futures are ass, i'm not arguing in their favor; for best results, look into virtual threads and use them in conjunction with futures

quaint basin
#

i see

thorn isle
#

e.g. your chunk loading example, with virtual threads, can be rewritten as

virtualExecutorService.execute(() -> {
    List<CompletableFuture<Chunk>> futures = new ArrayList<>();
    
    for (int dx = -viewDistance; dx <= viewDistance; dx++) {
    
        for (int dz = -viewDistance; dz <= viewDistance; dz++) {
    
            if (dx == 0 && dz == 0) {
                continue;
            }
    
            int chunkX = cx + dx;
            int chunkZ = cz + dz;
    
            futures.add(world.getChunkAtAsync(chunkX, chunkZ));
        }
        
    }
    
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    
    location.setY(newY);
    System.out.println("push: " + location + " to the buffer: " + Bukkit.isPrimaryThread());
    ringBuffer.push(location);
    
});
quaint basin
#

maybe a database pool and apis already built with completablefuture?

thorn isle
#

except you'd want to use an executorservice in place of a completablefuture for the top-level block

#

completablefutures have their place, as you can see i'm still using them and the allOf and other future style methods in the above example

#

the main difference is that instead of runAsync().thenX().thenX().thenAccept().exceptionally(), i.e. chaining futures, I use a virtual thread that blocks on each stage of the "chain"

#

this means i can use regular flow control and imperative-ish structures like helper methods and even recursion, all combined with futures

#

so yes, futures still have a place as e.g. the return type of a database query/api, or the chunk loading api in paper

#

it's just the way how you put them together that benefits from virtual threads

quaint basin
thorn isle
#

maybe, god knows, probably not measurably

quaint basin
#

The only downside is that I'll need an extra pool for this

thorn isle
#

it's a virtual thread so it doesn't really use a pool

#

you can also sort of do it without virtual threads if you target java 8 or whatever, but it will be far less efficient than only-futures, since it'll block a platform thread

quaint basin
#

oh i see

thorn isle
#

like an os thread, complete with its own allocation of memory for its call stack and other expensive things

#

now the os does also schedule os threads independently of hardware threads, so os threads are also a kind of "virtual" thread, but java virtual threads are much more lightweight

quaint basin
# thorn isle e.g. your chunk loading example, with virtual threads, can be rewritten as ```ja...

Currently, I create the executor like this: Executors.newVirtualThreadPerTaskExecutor(). My fear is that if this is called 1000 times, 1000 virtual threads could be created. Perhaps there's a way to limit the number of threads created, like in a fixed thread pool with OS threads, but I don't think that's necessary here because, from what I've seen, virtual threads are designed to support millions of virtual threads

thorn isle
#

the idea with virtual threads is that they block/sleep for most of their lifetime, and so are not mounted on os threads most of the time

#

meaning you can have 1000 virtual threads, and that is valid, as long as they are doing something like future.join(), io, or sleeping

slender elbow
#

you can create millions of VTs and your computer won't blow up

thorn isle
#

in the case that they all try to do work, yes, you will probably exhaust your platform threads

slender elbow
#

that's pretty much the point why they exist

quaint basin
thorn isle
#

no, because look at what the virtual thread does

#

it tells the server to load a bunch of chunks, and then it sleeps until they're loaded; then it puts a location into a queue

#

what it itself does is very lightweight, so it won't be mounted on an os thread for a meaningful amount of time

#

what you don't want to do is spawn 1000 virtual threads that all try to compute the 50,000th prime number, because those won't sleep and will hog all the os threads

slender elbow
#

well, only a handful of them will run at a time since they ultimately run on a regular fjp but yeah

#

most will just sit there waiting for their turn

thorn isle
#

i mean yeah, but the point is it will behave like starting 1000 regular threads computing the 50,000th prime number

#

so you lose all the advantages of the thread being virtual

quaint basin
# thorn isle so you lose all the advantages of the thread being virtual

That's because the operation you mentioned is very time-consuming (it should take minutes or hours), and the virtual thread will have the overhead of constantly switching threads, meaning it will end up being worse. Whereas with CompleteFuture in this case, there won't be this overhead of switching threads and it should execute faster. Now, for other options like the one you used, virtual threads are good because it's an operation that, in the worst case, should take seconds, which is nothing compared to calculating a 50,000th prime number. Did I understand correctly?

thorn isle
#

i probably phrased it a bit poorly; the concern isn't time, but compute

#

it's fine for a virtual thread to live for minutes; it's not fine for it to do work for minutes

#

it's fine for it to wait on a chunk being generated; it's not fine for it to do anything computationally expensive, like computing a prime number

quaint basin
#

Of course, if I use getChunksAsync and only get a response after 100 seconds, that's not computationally expensive even though it's a time-consuming operation. When I talk about operations that take time, I mean sync operations

#

Obtaining a response from the database is, I believe, faster than obtaining the 50,000th prime number. Therefore, I would say that accessing the database is computationally cheap relative to calculating that number

slender elbow
quaint basin
#

fair

slender elbow
#

accessing network resources like http call or database queries can take entire seconds and are not inherently computationally expensive

#

they might just take time due to network latency

#

and that is where VTs shine

quaint basin
slender elbow
#

they don't sit on a a whole ass OS thread waiting for the resource to become available

quaint basin
#

I think thread#sleep is the only sync case

slender elbow
#

the OS thread that manages the virtual thread is instead tasked with running other virtual threads

sly topaz
quaint basin
slender elbow
#

gha can eat tree bark

lilac dagger
#

You can have a fixed pool of vt factory

slender elbow
#

why would you pool VTs?

#

it makes no sense

lilac dagger
#

It is possible tho

slender elbow
#

sure

#

it is possible for you to eat tree bark as well

quaint basin
#

But that doesn't make sense xd

slender elbow
#

doesn't mean you should

sly topaz
#

Jakarta has a managed VT pool implementation for whatever reason as well, wonder if anyone uses that

quaint basin
sly topaz
quaint basin
#

They're the ones who created virtual threads, they should know in what situations they're useful, lol

slender elbow
#

uhhhhhhhhh

sly topaz
thorn isle
#

all enterprise shit is snake oil sold to The Suits™ to "increase productivity" that nobody actually doing any meaningful work needs or wants

sly topaz
#

that said, they aren't the same people who work on Java, Jakarta is a separate product

quaint basin
#

Okay. So I should use VTs to do database queries then?

#

instead of a fixed thread pool

slender elbow
#

to perform the queries, yes

thorn isle
#

anything that does IO or sleeps is natually a good target for VTs

#

just don't do compute on them

quaint basin
slender elbow
#

yes

#

connection pooling is separate from thread pooling

#

all you just did was eliminate the necessity for thread pooling

#

er, wording there is strange

quaint basin
#

Is connection pooling good for using VTS? I know Hikari does that

slender elbow
#

yes, connection pooling solves a different problem from thread pooling

thorn isle
#

much like how creating and destroying a thread is expensive, creating and destroying a connection is expensive

quaint basin
#

And Hikari probably doesn't use vts xd

thorn isle
#

so it makes sense to reuse threads and connections from a pool

slender elbow
#

hikari doesn't "use" VTs because hikari doesn't "use" threads

#

you use threads

thorn isle
#

except in the case of virtual threads, which are a transparent abstraction of "reusing threads from a pool"

tender shard
#

I recently refactored our base DatabaseProvider to use a virtual thread pool and everything broke

#

because the common fork pool is pretty smart and forgiving

slender elbow
#

whatever that means anyway

quaint basin
#

For example, Luckperms doesn't use virtual threads because it accepts Java 8 right?

quaint basin
thorn isle
#

either that, or run and join a future that runs on the correct thread to do the push

quaint basin
#

I basically need to make everything thread-safe: the pendingTasks variable; the HashSet structure into a chunkMap inside the LocationRingBuffer etc or then do the push and pendingTasks-- on main thread

#

I don't know if, even with that, the use of virtual threads is still worthwhile on this case

thorn isle
#

in this particular case no, VTs don't help too much; all it saves is one .exceptionally block

#

but you can make this work with VTs, and they will be invaluable with more complicated threading logic, like flow control and multiple or optional dependencies

#

i posted the vt example just to give an example on how to go from pure futures to virtual threads

#

the pure futures one with the .exceptionally block is fine

quaint basin
#

on this case obviously

thorn isle
#

perhaps marginally, but not measurably so

quaint basin
#

So here I would use VTs to have perhaps less performance (even if it's not measurable) and it would complicate the code considerably. Therefore, yeah, I don't think it's worthwhile here

#

But for example, I've already replaced my fixed thread pool with virtual threads and I know how they work, so in any case your code was a great help xd

thorn isle
#

VT's excel when there's flow control and complex dependencies, e.g.

//fetch dependencies in parallel
CompletableFuture<PlayerData> dataFuture = dataService.get(player);
CompletableFuture<Waystone> waystoneFuture = dataService.get(waystonePos);
PlayerData data = dataFuture.join();
Waystone waystone = waystoneFuture.join();

//legacy waystone; migrate
if (waystone == null) {
  waystone = new Waystone(...);
  dataService.save(waystone).join();
}

if (data.discoveredWaystones().contains(waystone.getUuid()) {
   //open waystone GUI
}
else {
   data = data.withDiscovery(waystone.getUuid())
   CompletableFuture<Void> dataModifyFuture = dataService.save(data);
   //play discovery animation
   PlayerData owner = dataService.get(waystone.getOwnerId()).join();
   owner = owner.withLog(player.getName() + " has found your waystone")
   CompletableFuture.allOf(dataService.save(owner), dataModifyFuture).join();
}
#

this would be a nightmare to do with pure completablefutures alone; you'd need several levels of nesting

#

but with a virtual "orchestrator" thread that fetches data from (possibly separate thread-run) systems and blocks only when necessary, it becomes much simpler

quaint basin
#

yea i see

young knoll
thorn isle
#

this is the click handler for clicking a waystone

#

since it runs async, it's free to fetch data, and then act upon it however it likes

young knoll
#

But then there will be a delay before my gui opens

thorn isle
#

there's a cache, but yes, in the worst case it's unavoidable

young knoll
#

Load the data on chunk load :c

thorn isle
#

since the data is on a remote db, and we can't open the gui before we know you can open it, there can be a delay

#

in most cases i solve this with "spinners" in the gui itself

#

but in this case since the branch is half-gui half-not-gui, that's not applicable

#

in practice however the delay isn't noticeable since the db is very close topographically

#

i opted against any sort of eager caching like prefetching the waystones on chunk load, since the db and the waystones are being modified by multiple backends at the same time

quaint basin
thorn isle
#

i honestly wouldn't bother

#

if you really have a 1 million squared map and are picking positions randomly, if someone places lava on the exact place where you picked a rtp position, and a player teleports to it and dies - god probably wants that player dead and there's nothing you can do

#

they'll probably be struck by lightning 100 times in a row even if you save them

quaint basin
#

https://imgur.com/a/n5yQdsj The problem with my implementation is that getHighestSafeBlockYAt loads the chunk, and in the chunkloadevent I remove the chunkkey loaded in pendingChunks. Therefore, the push will never be executed because my plugin has already loaded the chunk and removed pendingChunks key

#

If I had a way to know whether the chunk was loaded by my plugin or by a player, maybe I could solve this

#

But then there's the issue of a chunk being loaded by my plugin, and a player entering that chunk seconds after my plugin loads

thorn isle
#

i mean honestly i wouldn't bother listening to the chunk load

young knoll
#

Surely you can just check if the position is valid again before the actual teleporting

#

And if it isn’t throw it out and get another one from the buffer

thorn isle
#

that's alright as well, as long as your conditions aren't super strict

#

i.e. you can check for lava and skip the position if it's covered in lava, or solid blocks

#

but i wouldn't run more complicated checks like "must be within 32 blocks of a tree", as that'd defeat the point of precomputing the positions

#

but honestly i wouldn't bother even with the checks on teleport; in a 1 million squared world with say 100 points in the buffer, it's so incredibly unlikely for any of them to be invalidated that the player might as well win the lottery

#

i suggested this approach before when you originally asked about a rtp plugin like 6 months ago, but that was under the impression that you had a much smaller, like 1k across, world

#

i wouldn't bother even with a 10k squared world, let alone 1 million squared

thorn isle
#

well in my rtp plugin i want the player to have easy access to wood

#

so i require the position to be within 32 block range of a tree

quaint basin
#

+1 thing to my rtp plugin lol

young knoll
#

Pfft that’s no fun

#

They got legs that ain’t broken

thorn isle
#

spawns in the middle of a mega desert

quaint basin
#

I just have no idea how to implement this

quaint basin
thorn isle
#

searching for trees? i just brute force it

#

that's the advantage of precomputing your rtp points, it doesn't really matter how long it takes

quaint basin
thorn isle
#

yeeees

quaint basin
#

If the player builds a vanilla tree, then it's a valid position if near

thorn isle
#

that's fine, a tree is a tree

#

as long as they get wood

#

if you want to do it cheaper, just blacklist any biomes without trees

quaint basin
#

What if there is only one tree near that location?

thorn isle
#

4 logs is enough to get started

quaint basin
#

fair enough

#

And how do you know that the wood/leaves aren't buried?

#

or inside a house idk

#

like they can be hidden

thorn isle
#

well i just find a log within a 32 block range, iterate upwards and see if i find blocks following the order log -> leaves -> air -> top of heightmap

#

it probably doesn't work in some cases and maybe misses some trees but whatever

quaint basin
#

Player making a tree inside a house:

young knoll
#

When would a tree ever be buried anyway

#

Aside from someone making one

quaint basin
#

But yeah, I don't think I'll worry about it

quaint basin
thorn isle
#

stochastic correctness 💪

#

being correct 99% of the time is being right basically all of the time

quaint basin
#

I'll give a rank if the person spawns in an invalid block via rtp

thorn isle
#

that's the spirit

young knoll
#

Imagine someone rtps into lava placed after the location was checked and next to an buried tree

thorn isle
#

that does remind me

#

lava and "springs", i.e. lone source blocks, don't generate fully-flown, and loading a chunk through the api only keeps it loaded for a few ticks at most

#

so it's entirely possible for you to rtp underneath a natural lavafall and for the lava to flow into you if you don't move fast enough

young knoll
#

It’s a quick time event

thorn isle
#

i haven't ever seen this but it's theoretically possible

quaint basin
#

lol

young knoll
#

Just give them fire resistance for a bit

quaint basin
#

Do you check the blocks if they are valid when you teleport?

#

or you just ignore

thorn isle
#

i don't bother

young knoll
#

Alternatively you could load the chunk with a ticket for a while and then check for a safe place

thorn isle
#

i only have like 10k blocks across world and 50 positions in the buffer, but even then its incredibly unlikely for any of them to be invalidated

quaint basin
#

I have 1 million worlds so I don't have to worry about resetting the world bc is a resources world

thorn isle
#

supposing i had 100 concurrent players that all run the command every 10 minutes and place 1 lava block in the world per second, it takes 5 minutes for the buffer to completely cycle through, in which time 30,000 blocks have been turned into lava - out of the 100,000,000 blocks of the map; so there's a 0.03% chance for any given rtp point to have turned into lava

#

and that's with 100 players doing nothing but rtp'ing and running around with lava buckets

quaint basin
#

In my mind, prevention is better than cure. If someone loses items because of this, then I'm screwed

thorn isle
#

get IRP and just check the logs when someone does report it

quaint basin
#

Even worse is if someone loses because of this and has no proof, and I don't know if he's telling the truth or not

thorn isle
#

if the player did /rtp and a few seconds later died in lava, and the death pos is lava, they probably died from the rtp

#

in such a case just /irp restore the player

#

what we're talking about here is like trying to make sure UUID.randomUuid() doesn't by-chance generate the same uuid twice

#

if it does happen, be sure to get a lottery ticket

quaint basin
thorn isle
#

myeah

quaint basin
#

u prefer this than coreprotect?

#

never heard about irp

thorn isle
#

i haven't used coreprotect for a while (i forked prism a few years back), but IRP is complementary to a block logging plugin

#

irp snapshots the inventory on world changes, joins/quits, and before death

#

coreprotect back when i used it didn't really have inventory snapshots in any sense

#

in this case you'd use coreprotect to check if the block was lava at the time of death, and then irp to restore the lost items

quaint basin
#

So you're saying to add the location with timestamp the player teleports to in the RTP logs, and then if someone reports it, I can see that log with the timestamp and view it along with the IRP logs?

#

oh i see

thorn isle
#

i mean the command usage should be in your latest.log or whatever tar.gz logfile, as will be the death message

#

from there you can see when it was; then check if the block at that time was lava with coreprotect, and then restore the inventory of that death with irp

quaint basin
#

But if CoreProtect has inventory snapshots, why not use it now?

thorn isle
#

if it does, you can use it instead of irp

#

back when i used coreprotect, it didn't, but it's been a long time

#

the point is that you can just restore the inventory from a snapshot

quaint basin
#

In this case, coreprotect allows me to see when and where the player died, and I can then see if the location of the block where the player died was lava

#

And the locations of the nearby blocks if they are lava too

#

Anyway, I'll give invincibility for a few seconds when the player teleports to help with this and another things too

#

How does player#teleportAsync work? For example, if I type a command X that teleports me to location X using player#teleportAsync, but then another plugin, upon typing a different command Y, teleports me to location Y using player#teleport, is it possible that even though command Y was executed later, I might end up last in position X?

#

I think this results in weird bugs

thorn isle
#

no clue

#

the basic gist of teleportAsync is that it loads all the surrounding chunks async, before teleporting the player sync

#

i have no idea if there is any kind of "reservation" system to prevent the player from being teleported while the server waits on the chunk to load

#

that said i've never run into any issues with either

echo basalt
#

depends on the server impl

#

solid chance that modern versions of teleport just block teleportAsync

#

Folia would just throw an error on teleport

#

but I do think it'd use locks so they'd be sequential

quaint basin
echo basalt
#

I said block, not prevent

#

teleportAsync().join() blocks the thread

quaint basin
echo basalt
#

Depends on the impl

quaint basin
#

I'm talking about the implementation of the paper

echo basalt
#

Why would you ever want to join besides maintaining legacy compat

quaint basin
#

im on the latest version

slender elbow
#

joining teleportAsync on main is just going to cause a deadlock

young knoll
#

Yay

buoyant viper
buoyant viper
lilac dagger
#

nah

cloud perch
lilac dagger
#

chat gpt just happens to be a solution to our brain rotting away

#

correlation does not imply causation

hexed fulcrum
#

Hello! How can I get a list of all available Spigot versions programmatically? I know about hub.spigotmc.org/versions/ but is there a proper API for that, or is HTML parsing the only way?

mortal vortex
#

Versions, as in?

#

Minecraft versions? e.g, 1.21.1, 1.8, etc? or spigot builds?

hexed fulcrum
tender shard
#

but what do you need it for?

mortal vortex
#

Yeah but programmatically infers scraping or something, these are all heavily nested <tr>'s.

tender shard
#

it's nexus, nexus got an API I think

#

and nexus also provides an easy to parse XML file for snapshot versions

chrome beacon
#

Look at whatever BuildTools does and do that

mortal vortex
#

nah u right @tender shard

#
GET https://hub.spigotmc.org/nexus/service/rest/v1/search?repository=public&maven.groupId=org.spigotmc&maven.artifactId=spigot-api

this works

tender shard
#

I still wonder why they need that

last kiln
quaint basin
#

I'm doing this on @OnEnable method but this is null (i added WorldManager as soft-depend)

overWorld = Bukkit.getWorld("resources_world");

Why they load the worlds async?

worldly ingot
#

Is that World instance necessary on startup?

#

I don't imagine you specifically need it in onEnable(). If you're just checking for it in a Listener or something, reevaluate if you even need a World instance. If you want to do something as soon as that world is available, use a WorldLoadEvent listener instead

#

You may have to set your plugin's load time to STARTUP though because POSTWORLD is the default

#

I dunno. It depends ¯_(ツ)_/¯

shadow pumice
#

Hey! i've seen some plugins use a custom expiremental menus like this.. how can i read more abt it?

young knoll
#

They’re called Dialogs

shadow pumice
#

omg ty!

quaint basin
#

It's annoying because multiverse-core probably loads on onEnable, and I have to use two types of logic because worldmanager doesn't load the worlds when the plugin is enabled

quaint basin
chrome beacon
#

how else would it know if the chunk is generated

#

It needs to check if the chunk is on disk or not

quaint basin
#

Loading a chunk doesn't cause the chunk to be generated?

chrome beacon
#

Normally it does, but not with that method

#

Also that is Paper code I'm not sure why you're asking about it here

#

if you're not running Spigot you should be in the Paper discord

quaint basin
#

So if I use location#isChunkGenerated on a location where a chunk hasn't been generated, this method checks if the chunk is on the disk but doesn't generate the chunk?

chrome beacon
#

yes

quaint basin
#

Using this method at runtime is overkill then

glad berry
#

Hello @sullen marlin, I lost my Spigot account (the 2FA). How do I recover it?

vagrant stratus
#

?support

undone axleBOT
chrome beacon
chrome beacon
#

for spigot support*

vagrant stratus
#

pretty sure there's like... a two week ban or some shit too if they get the problem solved lol

umbral ridge
#

can you put pdc on items?

chrome beacon
#

yes

umbral ridge
#

perfect

#

instant response

glad berry
#

I lost my plugins lmao 🤣 inflatable

chrome beacon
#

people?

#

Sent straight to the shadow realm

glad berry
#

Do backup codes arrive in Gmail?

chrome beacon
#

No you got them when you enabled 2fa

chrome beacon
#

with big red text telling you to save them in a secure place and print them out

glad berry
#

Damn, I lost that phone

vagrant stratus
#

welp you're fucked

glad berry
#

lol v:

young knoll
#

If you email support from the email the account is bound to I think you can recover it

glad berry
#

Kk perfect ty

umbral ridge
#

I cant find the Registry for enchantments

#

where is it

#

oh its deprecated

young knoll
#

It’s not

umbral ridge
#

what's the best way to parse enchantments by a string?

chrome beacon
#

🔫

umbral ridge
chrome beacon
#

There you go

umbral ridge
#

getByKey is deprecated too, Registry.ENCHANTMENTS is deprecated too

#

endless trail of deprecation

chrome beacon
#

Read the deprecation note for enchantment registry

umbral ridge
#

god

chrome beacon
#

Same goes for getByKey

umbral ridge
#

all of this crap just for a simple enchantment parse

young knoll
#

On spigot it’s just Registry.ENCHANTMENT.match

umbral ridge
#

yea

#

cant use that

young knoll
#

¯_(ツ)_/¯

#

?whereami

umbral ridge
#

i like spigot and paper

quaint basin
#

https://pastes.dev/krDLmKAAF3 Am I doing this correctly to check if a location is in a chunk that has already been generated? I noticed that managedBlock is a blocking method and I believe it's not necessary here. Also, I think I can replace the use of getChunkAtImmediately with another method to get the chunk

chrome beacon
#

or did you just post that as reference

#

Also you really should head over to the Paper discord

quaint basin
chrome beacon
#

Just linked you the method to use

quaint basin
#

So, to check if a chunk was generated, should I use world#getChunkAsync#thenAccept(chunk -> if (chunk.isGenerated() { (...) })); ?

chrome beacon
#

yeah pretty much

quaint basin
#

i got it thanks

chrome beacon
#

or at least if I understand the docs correctly

#

tbh it's not entirely clear what happens when the chunk doesn't exist

quaint basin
#

It doesn't seem to be working as expected yea

#

chunk#isGenerated returns true even though it has never been generated (probably getChunkAsync generates it).

chrome beacon
#

Did you pass false to the generate param

quaint basin
#

oh no

chrome beacon
#

if you don't pass false it will generate

quaint basin
#

i see

#

chunk is returning null when doing this: world.getChunkAtAsync(location, false).thenAccept(chunk -> { (...) }));

If it has never been generated before, then will it be null?

#

Or could a chunk not be null but not be generated?

chrome beacon
#

You really should ask in the Paper discord though

quaint basin
#

I'm banned there

tender shard
#

lol

quaint basin
tender shard
#

I'm banned there because I sent a soundcloud link

umbral ridge
#

im banned there too

#

because i pissed off some mod or something

quaint basin
chrome beacon
#

Yeah that's fine

orchid brook
#

https://prnt.sc/5VLqjNSNM44O
https://prnt.sc/_e8Pa_DA0U1h
Hey, does anyone know how to create custom GUIs like these screenshots?
I'm trying to understand how some plugins manage to completely customize the player's inventory view sometimes they fully remove the default inventory interface, and other times they keep the inventory slots but replace all the items with custom GUI elements
What GUI framework (if any) do people typically use to achieve this kind of result?
And technically, how does it work ? are they using packets to hide/modify the inventory, or is there another approach?

vagrant stratus
#

resource packs

ivory sleet
#

additionally you may use custom item textures for icons etc

orchid brook
#

I know that they use resource pack but im talking about the fact they remove the player inventory part of the gui

ivory sleet
#

u can override vanilla gui

orchid brook
#

So no need to remove the bottom part of generic54 png and display back the bottom part when needed with packet ?

ivory sleet
#

eh I mean you can

#

but you can also just use a massive gui sprite

orchid brook
#

Yeah but i realy want to only render the top inv

#

that why

ivory sleet
#

then you might need to remove the bottom part of generic 54

#

or well, u could use another inventory type for that

#

doesnt have to be generic 54

orchid brook
vagrant stratus
#

Most UIs will end up looking like this once you get into resource packs lol

ivory sleet
#

but if u want that transparency effect, u likely need to erase it from an existing vanilla texture

orchid brook
# ivory sleet yea for example or lets say shulker box (iirc that has a separate sprite)

Yeah that could work but it limits me to only 3 rows of buttons for my custom GUIs. I think I'm going to try a different approach like removing the bottom part of a Generic_XX inside resourcepack, and then manually sending the player's inventory interface back to them via packet outside of my customs GUI by putting the player inv interface inside the title of any gui they open

This way I can support custom GUIs from 1 to 6 rows and only render the top inventory portion. The only downside I can think of is the hover highlight effect on the player inventory slots that I probably can't remove, but I think that's acceptable and can just be ignored

vagrant stratus
#

just look at the OriginRealms resource pack, see how they handle it lol

#

Obfuscated iirc, but just load it up as a ZipFile or whatever & save the contents to disk

ivory sleet
#

and then just reapply with a glyphed title

orchid brook
vagrant stratus
#

Easiest way to see how this stuff is done is to just... look at a server that does it already lol

vagrant stratus
#

depends tbf

ivory sleet
#

it can be hard to reverse engineer

umbral ridge
#

its not really the best to look at everyone else and see how its done, best practice is to do it yourself, think things through

vagrant stratus
umbral ridge
#

take time

vagrant stratus
#

9/10 times it'll be a glyph or invisible armor stand, or some sort of item with a custom display model

ivory sleet
#

but for more intricate things like player stable models etc, u might wna look into how existing servers do it

vagrant stratus
#

not sure how player stable models would be handled actually.
Though the Display entity certainly helps lol

wet breach
#

happens if I am doing something or working with something I am not super familiar with

tender shard
#

InventoryFramework is shit and introduces breaking changes with each release

#

It also still relies on obfuscated mappings IIRC

#

For nms stuff like anvil input

orchid brook
#

Yoo is jitpack down ? bruh

slender elbow
#

how unamusing and predictable

tender shard
#

yeah jitpack is shit

mortal vortex
#

who the fuck even owns jitpack

#

the site isnt explanatory at all