#development

1 messages · Page 63 of 1

icy shadow
#

whereas the 2nd will return 0

atomic trail
#

Gotcha, is any faster than the other for checking if the uuid is in the table?

river solstice
#

well the scenario with count would be something like:

try (ResultSet rs = st.executeQuery()) {
  if (rs.next()) {
    Long count = rs.getLong(1);
    // do whatever with the count of rows
  }
}

the select would be

try (ResultSet rs = st.executeQuery()) {
  while (rs.next()) {
    // do whatever with the rows, ex. get the column value for each row
    String foo = rs.getString("bar");
  }
}
atomic trail
#

That makes sense, ty

river solstice
#

if you intend to use the rows, go with SELECT *, if you only do the checking if it exists, go with SELECT COUNT(*)

#

since count technically should be faster

spiral prairie
#

That speed difference does not matter

atomic trail
#

Yeah I mean if I use count, it will create a default set of data for the player and then run SELECT * after anyway so doesnt make much sense to use it seems

#

Alright, also I assume it's fetch size that's 0 if the uuid isn't there? ```java
if(rs.getFetchSize() == 0) {
// Data doesn't exist
createDefaultData(uuid);
}

river solstice
#

fetchSize is not what you think

atomic trail
#

oh

river solstice
#

fetchSize is the amount of records will be fetched by jdbc

#

in (very) short, it's generally related to memory management

atomic trail
#

Oh okay, I won't touch that lol

#

Seems like I just need to check next()

river solstice
#

but I wouldn't use it in my work database with hundreds of millions of rows

icy shadow
#

if you want to do an upsert i think you can do that in 1 query

river solstice
#

@atomic trail if your UUID is marked as a unique key, then you can just use INSERT ... ON DUPLICATE KEY UPDATE ... (refer to docs for usage)

atomic trail
atomic trail
#

I'm getting the error java.sql.SQLException: ResultSet closed for this but not sure how?

#

It's from the bottom line

dense drift
#

you are still trying to access the balance even if the result set is empty

atomic trail
#

If it's empty

#

Ah the resultset isnt updated

dense drift
#

bingo

atomic trail
#

Does this update the resultset correctly? Currently testing but it's not woorking correctly. It runs like it's in a while loop or something lol, but might be something different

                ResultSet rs = ps.executeQuery();
                if(!rs.next()) {
                    LOGGER.error("Data doesn't exist");
                    // Data doesn't exist
                    createDefaultData(uuid);
                    rs = ps.executeQuery();
                }
icy shadow
#

🤨

atomic trail
#

Sec lol

#

Nvm this runs every tick or so for some reason... lol

    private void autoSave() {
        new BukkitRunnable() {
            @Override
            public void run() {
                LOGGER.error("Data has been autosaved");
                save(false);
            }
        }.runTaskTimerAsynchronously(orbitalTesting, 1, ConfigManager.getDataSaveInterval());
    }
#

I can fix this tho ofc, but ty

#

For some reason I'm getting this error, not sure why

[19:13:57 ERROR]: 
org.sqlite.SQLiteException: [SQLITE_BUSY]  The database file is locked (database is locked)
    at org.sqlite.core.DB.newSQLException(DB.java:1030) ~[sqlite-jdbc-3.36.0.3.jar:?]
    at org.sqlite.core.DB.newSQLException(DB.java:1042) ~[sqlite-jdbc-3.36.0.3.jar:?]
    at org.sqlite.core.DB.execute(DB.java:881) ~[sqlite-jdbc-3.36.0.3.jar:?]
    at org.sqlite.core.DB.executeUpdate(DB.java:922) ~[sqlite-jdbc-3.36.0.3.jar:?]
    at org.sqlite.jdbc3.JDBC3PreparedStatement.executeUpdate(JDBC3PreparedStatement.java:98) ~[sqlite-jdbc-3.36.0.3.jar:?]
    at net.foster.testing.orbital.data.Database.lambda$serializePlayerData$5(Database.java:150) ~[orbital-1.0-SNAPSHOT.jar:?]
    at java.util.concurrent.CompletableFuture$UniAccept.tryFire(CompletableFuture.java:718) ~[?:?]
    at java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482) ~[?:?]
    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) ~[?:?]
#

Line 150 is: ps.executeUpdate();

icy shadow
#

i suspect you're not closing your connections in some way

atomic trail
#

Probably true lol

#

How should they be properly closed?

icy shadow
#

by calling .close() when you're done with them

#

or even better, use try-with-resources

#

you should really be doing this with all the AutoClosables, i.e. the connection, statement, and resultset

dusky harness
#

Idk about connection tho

atomic trail
#

This is the code, ig I should just make a local variable for it

    public CompletableFuture<PreparedStatement> prepareStatement(String query) {
        return openConnection().thenApplyAsync(connection -> {
            try {
                return connection.prepareStatement(query);
            } catch (Exception e) {
                LOGGER.error("Error when preparing statement for query: " + query);
                throw new RuntimeException(e);
            }
        }, EXECUTOR);
    }
dusky harness
#

Bc u need that the whole time

#

Unless it's like a pool thing

#

Then idk how it works

#

I should learn how to use sql

atomic trail
#

But sounds ideal yeah lol

dusky harness
#

Google try with resourxws

#

It auto clpses

icy shadow
#

but also yeah, i think you can get away with keeping the connection open long-term

#

as long as the other two are closed

atomic trail
# icy shadow theyre things can be closed with try-with-resources

But I can't use try-with-resources for this can I?

            try {
                PreparedStatement statement = connection.prepareStatement(query);
                connection.close();
                return statement;
            } catch (Exception e) {
                LOGGER.error("Error when preparing statement for query: " + query);
                throw new RuntimeException(e);
            }

Or well I probably can somehow just not sure how it would be in this case

icy shadow
#

of course you can

#

try(PreparedStatement ps = connection.prepareStatement(query)) { ... }

#

oh

#

actually yeah it's a bit tricky in this case

#

you really have 2 choices

#
  1. dont close it in that method and just remember to close it whenver you use it elsewhere
#
  1. have this method take a lambda of what to do with the statement, then just return void
atomic trail
#

Either way I have to close it wherever I call the method?

#

A bit annoying to do all the time, but yeah I can do that

icy shadow
# icy shadow 2) have this method take a lambda of what to do with the statement, then just re...

i have a nice little example of this here: ```java
public <T> Future<T> query(String query, @NotNull CheckedConsumer<PreparedStatement> initializer, @NotNull CheckedFunction1<ResultSet, T> process) {
return Future.of(() ->
try(var con = dataSource.getConnection();
var statement = con.prepareStatement(query)) {
initializer.accept(statement);
var results = statement.executeQuery();
return process.apply(results);
});
}


(adjusted from using vavr so it might not be syntactically correct but you get the point)
#
 public Future<WarzonePlayer> load(@NotNull UUID id) {
        return database.query(
                "SELECT * FROM players WHERE uuid = ?",
                statement -> statement.setString(1, id.toString()),
                resultSet -> {
                    if (!resultSet.next()) {
                        return new WarzonePlayerBuilder().setPlayerId(id).createWarzonePlayer();
                    }
                    return loadPlayerFromResultSet(resultSet);
                }
        );
    }
``` then you'd use it like this
#

replace Future with CompletableFuture

atomic trail
#

I am not sure what you mean by CheckedConsumer

#

Just CompletableFuture as well?

#

Or CheckedFunction

icy shadow
#

theyre vavr types, just replacements of the standard ones except they can throw exceptions

#

just replace with the normal things & add exception handling urself

#

or actually it's probably better to define them urself

#

otherwise all the SQLExceptions will be really annoying to deal with

atomic trail
#

"Standard ones"? As in PreparedStatement and ResultSet?

icy shadow
#

what? no

#

Function and Consumer

atomic trail
#

Ohhhh lol ofc...

icy shadow
#

but you will get annoying exceptions if you do that

atomic trail
#

I'll create replacements now yeah

icy shadow
#

although they come with a bit of baggage

atomic trail
icy shadow
#

by throws Exception;

#

it just means your lambdas can throw exceptions

#

it doesnt exactly "handle" them

atomic trail
#

And Result

icy shadow
#

yeah it's called removing the method lol

#

and just copy the sneakyThrow impl

#

you kind of need it

#

or just wrap in RuntimeException

#

but not a fan of that

#

but really you only need the functional interface

atomic trail
#

But I can remove the applyCatching method?

icy shadow
#

yes

atomic trail
#

Gotcha, what is it actually used for?

icy shadow
#

well

#

a Result is just a more functional way of handling exceptions

#

which im personally fond of

#

so those methods are really just for convenience

#

im not sure im ever using them

#

but results in general are very handy

atomic trail
#

Very nice, is it possible to use statement inside resultSet? This explains it more ig

#

The exception handling is actually so nice to have

icy shadow
#

erm

#

Why do you need to do that lol

#

you have the results already

atomic trail
#

Ohhh yeah I'm dumb lol, mb

atomic trail
#

Okay I really don't understand what you did for the query method lol

icy shadow
#

of is just supplyAsync

#

generally when you’re doing functional stuff like this you can just compare signatures

#

look for a static function taking a lambda and returning a future

#

There aren’t many options

icy shadow
atomic trail
# icy shadow which bit in particular?

Oh I might understand now, like this right?

    public CompletableFuture<PreparedStatement> query(String query, @NotNull SafeConsumer<PreparedStatement> initializer, @NotNull SafeFunction<ResultSet, PreparedStatement> process) {
        return CompletableFuture.supplyAsync(() -> {
            try(Connection connection = Database.openConnection().get();
            PreparedStatement statement = connection.prepareStatement(query)) {
                initializer.consume(statement);
                ResultSet resultSet = statement.executeQuery();
                return process.apply(resultSet);
            } catch(Exception e) {
                LOGGER.error("Error when preparing statement for query: " + query);
                throw new RuntimeException();
            }
        }, EXECUTOR);
    }
icy shadow
#

looks about right

#

Would definitely put e into the runtimeexception though

atomic trail
#

Oh yeah lol, forgot that

icy shadow
#

Oh also

#

Put the resultset in a try-with-resources, forgot to convert that sorry

atomic trail
#

Can I make sure that the resultSet is run first?

#

Or well I need to check this

        this.query("INSERT OR IGNORE INTO " + OrbitalTesting.TABLE_NAME + " (uuid, balance) VALUES (?, ?)",
                statement -> {
                    statement.setString(1, uuid.toString());
                    statement.setInt(2, ConfigManager.getInt("start-money"));},
                resultSet -> {
                    if(!resultSet.next()) {
                        // Data doesn't exist
                        createDefaultData(uuid);
                    }
                    return null; //TODO: FIX
                }
        );
atomic trail
#

Well if the resultSet is empty I want to create the default data

#

Hence this

                    if(!resultSet.next()) {
                        // Data doesn't exist
                        createDefaultData(uuid);
                    }
icy shadow
#

what exactly is createDefaultData doing?

#

Because as far as I know resultsets are immutable

atomic trail
#

It was used like this before

        CompletableFuture<PreparedStatement> cfPs = query("SELECT * FROM " + OrbitalTesting.TABLE_NAME + " WHERE uuid = '" + uuid + "';");
        cfPs.thenAcceptAsync(ps -> {
            try {
                ResultSet rs = ps.executeQuery();
                if(!rs.next()) {
                    // Data doesn't exist
                    createDefaultData(uuid);
                    rs = ps.executeQuery();
                }

                double balance = rs.getDouble("balance");
                econ.getBalances().put(player, balance);
            } catch (Exception e) {
                LOGGER.error("", e);
            }
        }, EXECUTOR);
icy shadow
#

okay… so what do you want that method to return, if anything

atomic trail
#

Before I changed query

#

I just want to set a "template" for the players data basically

icy shadow
#

what you’re doing here is very odd

#

and not very efficient at all

atomic trail
#

Probably yeah lol

icy shadow
#

You’re doing 3 queries when you could do this whole thing in 1

#

Look into “SQLite upsert”

#

Or possibly 2

#

This isn’t exactly an upsert

atomic trail
#

I'm doing it in 2 right now

icy shadow
#

well no

#

You’re doing 3, since you’re executing the lookup twice

atomic trail
#

I think I'm just doing it twice, but not too sure

#

Also a bit confused what to return in the resultSet

icy shadow
#

that does seem like ur doing the same thing twice yeah

icy shadow
#

you dont wanna use query for inserts because there is no resultset

#

what i did was had a separate method CompletableFuture<Void> execute(String query, @NotNull SafeConsumer<PreparedStatement> initializer)

#

and then you just do statement.execute(); and return null

#

no resultset

atomic trail
#

And yeah thanks for all the help, it's beginning to make more sense lol

icy shadow
#

np np

#

we'll have you monadding in no time

atomic trail
#

monadding? But yeah definitely lol

atomic trail
lyric gyro
atomic trail
lyric gyro
atomic trail
#

Uhhh how do I avoid the data being added to a new column every time? There should only be one column for every uuid ofc

spiral prairie
worn jasper
stuck canopy
dusky harness
worn jasper
worn jasper
#

not even the forums are helping 💀

dusky harness
#

¯_(ツ)_/¯

#

and if the project isn't public then u can make a minimal reproducible example

worn jasper
#

Yeah its not in github, will try to recreate it and will send it here tmr

royal hedge
hoary scarab
#

Apparently the previous hash on bitcoin block chain is little endian...
000000000000000000034934f36ef2518fbe17b53922cddd6dc849d789f06bf3 from block 807,746

But the pool I'm querying gave me
89f06bf36dc849d73922cddd8fbe17b5f36ef251000349340000000000000000

I tried simply reversing the string which didn't work. I also tried splitting the string every 2 chars and reversing which didn't work either. I know I am probs missing something but can't figure out what.

torpid raft
#

it's reversed but in blocks of 8 characters

#

00000000 00000000 00034934 f36ef251 8fbe17b5 3922cddd 6dc849d7 89f06bf3
89f06bf3 6dc849d7 3922cddd 8fbe17b5 f36ef251 00034934 00000000 00000000

#

since it's hex i guess it's split into 32 bit blocks

hoary scarab
#

Whats the easiest way to check the char per block? (Incase it ever changes)

torpid raft
#

wdym check char per block

#

like comparing two 32bit blocks to see if they are the same?

hoary scarab
#

I think I read your message wrong... It should always be 8 chars per block correct?

torpid raft
#

yeah probably

hoary scarab
#

👍 and it worked. Thank you.

torpid raft
#

nice 😌

#

it might change if they ever change the hash algorithm they use

hoary scarab
#

I doubt it ever will.

torpid raft
#

yeah realistically it's fine

hoary scarab
#

If I use something other then bitcoins block chain it might.

atomic trail
#

Can someone help me with ACF please, I'm trying to make an optional target for a /balance command, but the target is always just the sender, here's the code I have atm

    @CommandAlias("balance|bal")
    @Syntax("[player] &e - Check balance of player (default is sender)")
    public void onBal(CommandSender sender, @Optional Player target) {
        if(target == null) {
            if(sender instanceof Player) {
                String balance = numberFormatUS.format(econ.getBalance((Player) sender));

                sender.sendMessage(
                        ChatColor.translateAlternateColorCodes('&',
                                Objects.requireNonNull(commandsSection.getString("balance.no-target"))
                                        .replace("%balance%", balance)));
            }
            return;
        }

        String balance = numberFormatUS.format(econ.getBalance(target));
        sender.sendMessage(
                ChatColor.translateAlternateColorCodes('&',
                        Objects.requireNonNull(commandsSection.getString("balance.target"))
                                .replace("%target_player%", target.getName())
                                .replace("%balance%", balance)));
    }
icy shadow
#

use OnlinePlayer instead of Player

#

iirc Player always resolves to the sender

icy shadow
atomic trail
#

It should be effectively the same, but still

atomic trail
icy shadow
icy shadow
atomic trail
#

I'm not really using Streams or Optionals I think haha

icy shadow
#

☹️

river solstice
#

no streams no optionals? L

icy shadow
worn jasper
#

can't find anything about setting the class loader though

sterile hinge
#

set the context class loader

#

before initializing the Mapper

icy shadow
#

Classic

worn jasper
#

should I set it, initialize it and then go back to the old class?

sterile hinge
#

yes and yes I guess

worn jasper
# sterile hinge yes and yes I guess
Thread t = new Thread(() -> {
  datastore.getMapper().mapPackage("me.mrafonso.bdiscord.database.entity");
});
t.setContextClassLoader(plugin.getClass().getClassLoader());
t.start();

would this work?

sterile hinge
#

but you probably need to do it before, because it needs to happen before the mapper is created

worn jasper
#

lol

#

it still can't find my class

#

well apparently it's relative path

#

so I made it just "database.entity"

#

but still receive this message: [dev.morphia.Datastore] No classes have been mapped.

atomic trail
#

Is it possible to add a command to CommandHelp for ACF? So this

    @HelpCommand
    public void onHelp(CommandSender sender, CommandHelp help) {
        help.showHelp();
    }

Will also show this command for example

    @CommandAlias("setbalance|setbal")
    @Subcommand("target amount")
    @Syntax("<target> <amount> &e - Give target player money")
    @CommandPermission("orbital.operator")
    public void onSetBal(CommandSender sender, String[] args) {
        // Some code
hazy nimbus
#

tbh I suggest you go ask in their discord

#

prolly gonna get a better answer there

atomic trail
#

They dont have one I believe

hazy nimbus
#

They do

#

Do you want to send a link in DMs?

atomic trail
#

Yes please

stuck canopy
#

what Entity interface have the getEquipement method?

stuck canopy
#

ah LivingEntity

hazy nimbus
#

Apparently LivingEntity too

#

wtf is the difference betwen them

#

oh lol

#

mob inherits from livingentity

#

I wonder why the javadocs suggest that it's directly declared in mob

stuck canopy
hazy nimbus
#

it is

stuck canopy
#

yea

proud pebble
hazy nimbus
#

I would send a screenshot if I could 💀

proud pebble
hazy nimbus
#

ikr

#

But if you take a look at the mob javadocs

#

you can see that the method getEquipment is not listed under "Methods inherited from interface org.bukkit.entity.LivingEntity"

#

so I assummed it was declared there

dense drift
#

It is listed

hazy nimbus
proud pebble
#

:/

grim oasis
#

bad paper docs 🤷‍♂️

hazy nimbus
#

it's kinda stoopid

proud pebble
#

what the hell did paper do to break their javadocs

grim oasis
hazy nimbus
#

I didn't look there

#

cuz I was lazy

grim oasis
#

very weird

hazy nimbus
#

tho why do they still use the jdk 8 style javadocs?

proud pebble
#

spigot does too

#

idk what the newer javadocs look like

hazy nimbus
#

hm

#

I swear I saw some new fancy javadocs site somewhere

#

can't find it now tho

#

apparently I was just hallucinating 💀

grim oasis
#

maybe you're just designing the new javadocs in your dreams

hazy nimbus
#

perhaps

proud pebble
#

i wonder if the reason im getting clientside lag is cus im sending lots of section packets instead of sending chunk packets

#

also im trying to figure out if i should have a map of chunk then section instead of one map that has all the block positions

#
    ChunkX      ChunkZ      SectionY    SectionRelativeBlockPosition
Map<Integer,Map<Integer,Map<Integer,Map<Short,Material>>>>

Map<BlockPositon,Material>
#

ofcourse for the massive map i have ill end up making objects for their specific storages

#

like it would be more simple with just 2 values but the massive map of values would make lookup times alot shorter (i think idk)

sterile hinge
#

why not Map<ChunkKey, Section[]>

dense drift
#

A map with 3 other maps looks like a bad design D:

hazy nimbus
#

but yeah

#

what's the XY here?

river solstice
#

triple map nesting

#

this would better not appear in my job during code review

#

you would have some shit to fix

dense drift
#

But they are HashMaps, the code has to be very fast!!

hazy nimbus
#

but that's cursed either

pulsar ferry
atomic trail
#

Currently having issues with TriumphGui if someone can help. When this runs it prints the player correctly and no errors or anything, but the gui isn't opened

    public void openSelectionGui() {
        LOGGER.error("Selection gui has been opened for " + player.getName());
        selectionGui.open(player);
    }
selectionGui = guiManager.setupSelectionGui(this);

setupSelection: https://paste.helpch.at/pehemeqixo.csharp

#

Does anyone have an idea why?

pulsar ferry
#

We probably need more context

#

Hard to tell anything is wrong from what you sent

atomic trail
#

Ah yeah I should probably have told you this. It works when I first join and use it (after server restart). But if I then leave the server and join back, the gui wont open. It works again after server has been restarted but yeah

#

Not sure what can cause this

#

It's run from this command

    @Default
    @CommandAlias("kits|kit")
    public void onKitsMenuOpen(Player player) {
        BePlayer bePlayer = playerManager.getBePlayer(player);
        bePlayer.openSelectionGui();
    }

But the BePlayer is not null either or it would give an NPE so not sure what's wrong

minor summit
river solstice
#

maps are O(1), 1 * 3 is still O(1) !!! big speed, fast!

warm steppe
#

if so, try saving uuids or names

icy shadow
proud pebble
#

the whole point is so that i dont have to have hundreds of player mines stored on disk and all of them can be at 0,0 which means less chunks loaded aswell

dusky harness
#

anticheats might not like that tho

#

🥲

proud pebble
#

yeah ive thought about that

#

hears a fix 🌈 No Anticheat 🌈

dusky harness
#

💀

proud pebble
#

i might beable to hook into an anticheat and disable some of the checks when they are in the mine world since flight would be enabled anyways

#

probs the phase checks

cerulean birch
#

doesnt grim have simulated per-player worlds or something, sounds like it might sort of work with your system

proud pebble
#

im sure ill get to that as development continues

cerulean birch
#

probably

proud pebble
#

tbh thinking about it i might not even have to store the materials in memory at all, since i wont be saving them as whats inside the mine doesnt matter since its all cosmetic anyways

atomic trail
atomic trail
warm steppe
icy shadow
#

The uuid will be the same

#

Assuming that’s being used as a key

hoary scarab
#

How would I compare hash and target in java?
Python I can just do hash < target

Example;```
Hash: cdd88b5d653fd2eb19e773cca822d080a118f1d543e4a03f4277f43f6c4bcfbf
Target: 0000000000000000000532ae0000000000000000000000000000000000000000

icy shadow
#

I mean it depends on the type

#

But that’s just compareTo

hoary scarab
#

Ah thought I would have to convert to integer or something. I'll try that thanks

#

I think I'm misunderstanding the description...
Would I use hash.compareTo(target) > 0?

icy shadow
#

other way around

#

0 = equal, negative = less than, positive = greater than

atomic trail
signal grove
#

any way i can register a listener class, but ignore certain listeners inside it if they dont exist in the current version?

#

or will i have to split it up into separate listener classes

icy shadow
#

almost definitely the latter

signal grove
#

ok, thanks

signal grove
#

just curious, what are you doing with hashes that requires you to check anything except the compareTo being 0

#

unless it's some sort of locality sensitive hash, the < or > is probably useless info

torpid raft
#

bitcoin stuff

icy shadow
#

Decrypting the blockchain

signal grove
#

xD

hoary scarab
#

^^^

worn jasper
hoary scarab
#

Been editing a mining script for a bit but figured I would port it to Java. (Maybe even c++ eventually) having issues getting the hexing right though.

icy shadow
#

lol

#

You’re gonna make negative money

hoary scarab
#

I don't care about making money with it. Just want to do it

atomic trail
torpid raft
#

minecraft plugin bitcoin miner 🤯

#

people can finally make money from their vanilla survival servers averaging 0.03 active players

dusky harness
atomic trail
#

Yeah it is, but apparently it's as it should be

#

Brister Mitten helped a lot yesterday and this is what I ended up with

#

So I assume it's right

dusky harness
#

oh ok

atomic trail
#

It's because the database is local and not remote I believe

dusky harness
#

but ig it could be static ye

pulsar ferry
#

Just because it could doesn't mean it should

dusky harness
#

tru

atomic trail
#

Wait yeah making it static wouldnt even fix my original issue

dusky harness
atomic trail
#

Waittt I should only have one connection for writing it seems
Sqlite takes care of the file level locking. Many threads can read, one can write. The locks prevent more than one writing.

worn jasper
#

or wait

icy shadow
#

But also yeah don’t make it static

#

You’re losing a lot of refactoring potential if you do

#

most notably being able to move this to an interface so you can make a dummy implementation for testing

worn jasper
#

does #containsAtLeast() check offhand too? or only inventory?

royal hedge
worn jasper
#

hmm okay

#

also, random question, any way to fix the inventory click event being canceled not being properly canceled? like, if you click enough times with a good timing, the item stays in the slot it's not supposed to...

#

and it's not visual bug

royal hedge
worn jasper
#

even with priority in highest

pulsar ferry
#

Are you in creative?

worn jasper
#

nop survival

pulsar ferry
#

Should not happen in survival
Which version?

worn jasper
#

1.20.1

#

you have to click a bunch, sometimes it works, sometimes it doesn't

royal hedge
#

I still think it's a ghost item

worn jasper
#

but it stays there

pulsar ferry
#

Most likely a ghost item, but even then if you're in survival it should get removed

worn jasper
#

kinda doubt

#

let me try it out with a flint and steel

#

if it's a ghost item

#

it shouldn't light anything, right?

#

lol def. not a ghost item

#

even loses durability

royal hedge
#

isnt durability calculated client side as well

worn jasper
#

unsure, but either way an item shouldn't function if it's a ghost item, no?

royal hedge
#

ye it wouldnt

worn jasper
#

once you try to interact with it, it usually disappears

#

so it def. "bypasses" canceling the event

#

question is if this is an issue on paper/spigot, minecraft, or myself?

#

but I am just checking if the item I am putting in offhand is similar to X and if so, canceling it

#

nothing much

royal hedge
#

i think this is whats happening:

  1. u put the item in the slot
  2. u picked it out of the slot
  3. the server receives the #1 and puts it back in your cursor
  4. the server receives the #2 and puts it back into the inventory
worn jasper
#

that does make sense but can I do something about it? lol

#

since I assume that relies on timing of things

#

this looks like something out of my control?

#

could be wrong ofc

leaden sinew
worn jasper
# leaden sinew Send your code
@EventHandler(priority = EventPriority.HIGHEST)
public void onClick(InventoryClickEvent e) {
    if (e.getSlot() != 40) return;

    assert normalItem != null;
    assert compactItem != null;

    if (e.getCursor() != null && (e.getCursor().isSimilar(normalItem) || e.getCursor().isSimilar(compactItem))) {
        e.setCancelled(true);
    }
}

normalItem and compactItem being 2 different ItemStacks that are the items I don't want to allow

pulsar ferry
#

Assert monkaS

leaden sinew
#

Why do you have assert?

worn jasper
#

purely for testing, those items are being fetched from a config file on start

#

lol

#

(but they are not null for context)

leaden sinew
#

Assert doesn’t do anything

pulsar ferry
#

Btw highest priority is the lowest lol

worn jasper
worn jasper
#

.-.

#

now I am confused

pulsar ferry
#

Bukkit's naming is dumb, highest importance runs last, it's not really priority (imo) so lowest runs first

worn jasper
#

bruh

#

but can that be the cause though?

leaden sinew
#

Probably not

pulsar ferry
#

Probably not, just letting you know

leaden sinew
#

What happens if you just cancel slot 40 and don’t check items?

worn jasper
royal hedge
#

except i think it should be an int and PostOrder a utility class

worn jasper
#

lol

@EventHandler(priority = EventPriority.LOWEST)
public void onClick(InventoryClickEvent e) {
    if (e.getSlot() != 40) return;
    e.setCancelled(true);
}
#

it still bypasses it

royal hedge
#

are you able to keep track of what is supposed to be in the offhand?

worn jasper
pulsar ferry
#

What are you doing btw? Just open a custom inventory and spam it?

worn jasper
#

not even custom inventory

#

player inventory

#

I am canceling the click in the offhand slot

#

of the player inventory

#

and if you spam it, it still somehow bypasses it

#

and it's def. not a ghost item

#

any chances the event is being called so many times, paper/spigot can't handle them all? lol

leaden sinew
#

Is it a localhost?

leaden sinew
#

Sometimes when you click, you actually drag the item

worn jasper
worn jasper
blazing pewter
#

anyone here know how to port rendering code from fabric 1.19.4 to 1.20.1

atomic trail
icy shadow
#

You can probably get away with just one connection if you’re not doing loads of concurrent reads

atomic trail
#

But it is possible to use more than one connection? How?

#

By checking if anything is already writing before doing anything?

worn jasper
icy shadow
#

I think

#

I am far from an expert on concurrency so this might be a terrible idea

atomic trail
#

Just creating one connection for writing I think lol

icy shadow
#

That won’t prevent concurrent writes

#

necessarily

#

You need a mechanism to prevent multiple threads from using the connection at once

#

Assuming you have multiple threads

icy shadow
worn jasper
#

Any ideas how to disable this log using morphia in an mc plugin?

#
Logger mongoLogger = Logger.getLogger("org.mongodb.driver");
mongoLogger.setLevel(java.util.logging.Level.WARNING);

tried this but it didn't work

atomic trail
#

I havn't used a lock before so not sure if this is how it should be implemented, is this correct?

    public CompletableFuture<Void> update(String query, @NotNull SafeConsumer<PreparedStatement> initializer) {
        ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
        Lock writeLock = lock.writeLock();
        return writeConnection.thenAcceptAsync(connection -> {
            writeLock.lock();
            try(PreparedStatement statement = connection.prepareStatement(query)) {
                initializer.consume(statement);
                statement.executeUpdate();
            } catch(Exception e) {
                LOGGER.error("Error when preparing statement for query: " + query, e);
                throw new RuntimeException();
            } finally {
                writeLock.unlock();
            }
        }, EXECUTOR);
    }
wheat carbon
#

what are you trying to do?

#

make sure only one thing is done at a time on a connection?

#

i've never used locks, I'd just use a single thread executor

#

idk what's better per say

atomic trail
#

Trying to not write to database from multiple threads at the same time

#

But yeah a single thread executor would work as well I guess

wheat carbon
#

yeah what I'd do is use a single thread executor and get rid of the lock stuff

#

and then use the sync version of this method writeConnection.thenAcceptAsync

#

in the task u schedule in the executor

atomic trail
#

I prefer having to just use one executor though. Because when saving I want to check if tasks are still running etc.

icy shadow
#

The lock needs to be global

#

if you’re doing that

#

and since you can use multiple threads for reading, yeah it makes sense to have a multi thread executor still

#

Tbh it might be easier to just do synchronized with some object as the lock

atomic trail
icy shadow
#
private final Object writeLock = new Object();

private Connection writeConnection = ...

void writeSomething() {
  synchronized(writeLock) {
     // do shit
  }
}
#

something like this

atomic trail
#

Ehhh not really sure what that would do tbh, I think I'm sticking with the Java Lock system, seems simple enough lol

#

Would it work more efficiently or?

icy shadow
#

Synchronised is a lock

#

Yes

#

It’s the most primitive lock

atomic trail
#

Ohhh so actually just

    CompletableFuture<Connection> writeConnection = new CompletableFuture<>();
    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    private CompletableFuture<Void> update(String query, @NotNull SafeConsumer<PreparedStatement> initializer) {
        synchronized(lock) {
            return writeConnection.thenAcceptAsync(connection -> {
                try(PreparedStatement statement = connection.prepareStatement(query)) {
                    initializer.consume(statement);
                    statement.executeUpdate();
                } catch(Exception e) {
                    LOGGER.error("Error when preparing statement for query: " + query, e);
                    throw new RuntimeException();
                }
            }, EXECUTOR);
        }
icy shadow
#

I mean

#

That’ll work but

#

When I wrote Object I literally meant that

#

You don’t need a lock object AND synchronized

atomic trail
#

How can it just use an empty object though? Does it just store the information in that object?

icy shadow
#

it’s just a sort of marker, it can be anything. Object is usually used though

#

as far as I know it’s based on reference equality

#

It creates a lock marked with a reference to the object, then that gets compared

atomic trail
#

Can't I just use this then? Or well I guess I can, but would that make it slower?

icy shadow
#

I suppose so

#

If you lock on "this" then any other code which knows about your object might choose to lock on it. While it's unlikely to happen, it certainly could do - and could cause deadlocks, or just excessive locking.

Ok yeah maybe don’t do that

atomic trail
#

Ahhh yeah I will just do it on Object lol

sterile hinge
#

note that monitors currently don't work well with virtual threads, and it's uncertain how it will be in future

#

likely doesn't matter in your case I guess

#

but then, you can also just synchronize on the instance itself/make the method synchronized

icy shadow
#

That’s weird, you’d think it would be a pretty trivial change

atomic trail
#

Virtual threads? And I assume the monitor is what's checking if the lock is locked or unlocked in the object?

icy shadow
#

Making the method synchronized would need a bit of refactoring since part of the method is running supplyAsync, you wouldn’t actually be synchronising the right thing

sterile hinge
atomic trail
# icy shadow Making the method synchronized would need a bit of refactoring since part of the...

Shouldn't synchronise be inside the cf then?

        synchronized(lock) {
            return writeConnection.thenAcceptAsync(connection -> {
                try(PreparedStatement statement = connection.prepareStatement(query)) {
                    initializer.consume(statement);
                    statement.executeUpdate();
                } catch(Exception e) {
                    LOGGER.error("Error when preparing statement for query: " + query, e);
                    throw new RuntimeException();
                }
            }, EXECUTOR);
        }
icy shadow
#

uh

#

yes with a big asterisk

#

maybe

sterile hinge
icy shadow
#

i dont really like having a global CF variable

icy shadow
atomic trail
# icy shadow yes with a big asterisk

As in like this

            return writeConnection.thenAcceptAsync(connection -> {
                synchronized(lock) {
                    try (PreparedStatement statement = connection.prepareStatement(query)) {
                        initializer.consume(statement);
                        statement.executeUpdate();
                    } catch (Exception e) {
                        LOGGER.error("Error when preparing statement for query: " + query, e);
                        throw new RuntimeException();
                    }
                }
            }, EXECUTOR);
atomic trail
icy shadow
#

yeah uh

sterile hinge
atomic trail
#

Sooo essentially I shouldn't use the monitors then and just use a lock? Lol

icy shadow
#

what you might wanna do is refactor it into some method like T withLockedConnection(Function<Connection, CompletableFuture<T>>) where you handle all the locking in there

atomic trail
#

Only using the locking for the update method so I think it's alright without refactoring it

icy shadow
#

good point

atomic trail
#

lazily initialize some global variable? Not sure what this means

icy shadow
#

dont create the connection until you actually need to

#

but only create it once

atomic trail
#

What does the waits in get actually mean? Does it block the thread it's on until it's completed?

atomic trail
#

Modified the update method

    private CompletableFuture<Void> update(String query, @NotNull SafeConsumer<PreparedStatement> initializer) {
        Lock writeLock = lock.writeLock();
        return CompletableFuture.runAsync(() -> {
            writeLock.lock();
            try(PreparedStatement statement = writeConnection.prepareStatement(query)) {
                initializer.consume(statement);
                statement.executeUpdate();
            } catch(Exception e) {
                LOGGER.error("Error when preparing statement for query: " + query, e);
                throw new RuntimeException();
            } finally {
                writeLock.unlock();
            }
        }, EXECUTOR);
    }

Reverted back to the Java Lock class, just to be safe

icy shadow
#

but suit yourself

#

also fix RuntimeException

atomic trail
atomic trail
atomic trail
icy shadow
#

virtual threads dont actually exist yet

#

well

#

unless you're on a preview version of java

icy shadow
#

yeah i mean, that's a very far-future thing

#

unless you plan on switching to java 21+ AND switching to virtual threads

#

it's probably not something you need to worry about

royal hedge
#

it is

#

you should plan to switch to java 21 and virtual threads

icy shadow
#

im not sure thats necessarily true

#

particularly not in this context where you're doing a lot of IO

#

with kotlin coroutines you're actually recommended not to use coroutines for IO

#

in general yes, though

minor summit
#

i mean virtual threads aren't exactly useful for file IO since you can't do them ops in a non blocking way like with, say, sockets

#

it'll just spawn a new carrier thread

#

by "useful" I mean fully take advantage of them

icy shadow
#

^^

pulsar ferry
#

Why does it matter? It's just logging once that the client is created

worn jasper
#

it spams the console which isn't ideal

pure crater
#

I've been figuring out ways to change the fps based on the task. In short, here is a simple step by step process of what I'm doing:

  1. Store a previous variable for the current milliseconds
  2. Create a while loop that runs forever until completion
  3. Within that runnable, check if the time passed by subtracting previous milliseconds from current is less than 42. If so, do continue and wait for the number of milliseconds to pass.
  4. If 42 milliseconds passed, call the paint function, which will grab the frame and display as necessary.

As for a code example:

private void paintLoop() {
    final long previous = System.currentTimeMillis();
    while (this.running.get()) { // checks for completion
      try {
        final long current = System.currentTimeMillis();
        final long elapsed = current - previous;
        if (elapsed < 42) { // this is around 24 fps
          continue;
        }
        this.paint();
      } catch (final IOException e) {
        this.neon.logConsole(e.getMessage());
      }
    }
  }

The reason why I chose 42 milliseconds is because this approximantly 24 frames per second, since there are 1000 milliseconds and 1000/24 is 41.6666 or almost 42 milliseconds for each frame to show.

The issue with this is that it assumes that frames will always be 24 fps. In reality, many video games and video players have fluctuating frames per second to adjust based on how fast it takes for the frame to be processed/painted. I've explored solutions like buffering frames into a queue, but I couldn't find anything. does anyone know some possible algorithms?

pulsar ferry
hazy nimbus
#

Or

#

You could deploy a log filter

#

and filter it there

#

but that's just a workaround that could be exploited

stuck canopy
#

pretty sure there is a #setDamage method for that

#

yep

#

Damageable#setDamage

open pilot
#

how do i make a item show as being "enchanted" whilst not actually having any enchants

dense drift
#

You can not, you can enchant it and then use the HIDE_ENCHANTS item flag

craggy zealot
#

"INFO java.io.FileNotFoundException: ./testWorld/data/raids.dat (No such file or directory)"

#

im creating a world that will be deleted but after i delete it it gives me this

#

I do set auto save off

#

full error message

snow dragon
#

I need some help anyone here

sterile hinge
#

Just ask your question

neat pierBOT
#
New Reminder Set!
In 4 hours, 59 minutes, 59 seconds:

continue working

minor summit
inland walrus
#

there is an error in my code, I can't figure out what it is. the console is showing a null error.

#

I will send you the error now

inland walrus
gilded imp
#

what took you one hour

dusky harness
#

Lol

river solstice
river solstice
mellow pond
#

Also don't use spigot

warm steppe
#

why isnt spigot like deprecated or smth?

dense drift
#

Because it is not the case? Paper still depends on it and it cant just be shut down

hoary scarab
#

Same reason bukkit isn't. People are keeping it updated and just making forks of it.

icy shadow
#

lol not a single soul has cared about bukkit for years

#

As far as I know it’s effectively it’s renamed spigot now at this point too

hoary scarab
#

I still see questions about it from time to time

sterile hinge
#

because people don't understand that they use spigot

strange remnant
#

i use spigot?

icy shadow
#

maybe you do

river solstice
hoary scarab
#

I still use spigot 😉

icy shadow
#

good for you

worn jasper
#

imagine

worn jasper
#

quick question, if I am using JDA in an mc plugin, would it be wise to execute jda code async? (using bukkit's scheduler)

spiral prairie
#

yes

dusky harness
#

well

#

if you need the scheduler then sure

#

but JDA already has its own async system that you can use

spiral prairie
#

isnt jda async anyway with the queue shit?

dusky harness
#

yea

dusky harness
spiral prairie
#

because you can use jda async

dusky harness
#

they meant handling async themselves using bukkit scheduler

spiral prairie
#

eh not really

dusky harness
#

they said

using bukkit's scheduler

spiral prairie
#

just said if they can execute code async there

#

not to replace the queue with complete and be done with it

dusky harness
#

async using bukkit's scheduler, not in scheduler

but anyways, even for non-JDA stuff I'd recommend not using the scheduler unless you need to use the game ticks

spiral prairie
#

why

dusky harness
#

since you'd have to wait for the next tick

spiral prairie
#

the scheduler is a good way for the server software to manage all tasks without you using java executors

#

because afaik that can cause a few issues but not sure

dusky harness
#

manage all tasks
if you want repeating tasks every x ticks, then yes I recommend using it

#

but if you just want to run async code in general I wouldn't

spiral prairie
#

and also async tasks

#

you have to remember that youre not the host and that the server is very sensitive to async stuff

#

but i could be talking bullshit :=

dusky harness
#

why is it very sensitive
also note that the bukkit scheduler also uses java executor

dusky harness
#

that doesn't run on ticks

dusky harness
#

lol
since if u have like 30 plugins that all manage their own async, that's a lot of threads

spiral prairie
#

exactly

dusky harness
#

but bukkit doesn't have that unfortunately 🥲

spiral prairie
#

if every plugin made an async executor

orchid mural
#

Async tasks already use a thread pool

dusky harness
#

since currently, you can only run it on the tick loop

#

¯_(ツ)_/¯

pulsar ferry
#

Well minecraft is tick based so it makes sense that async tasks would also be tick based

dusky harness
#

true but with the amount of plugins that a server can have, I wonder how many threads a server has 🤔

pulsar ferry
#

That is not how it works, async tasks are run on a threadpool it'll only make a new thread if it needs one, there is normally only a few threads

graceful hedge
#

think it could be as simple as just exposing the cached thread pool through bukkit scheduler :>

#

like with getMainThreadExecutor

dusky harness
#

bukkit currently doesn't have that, so I wonder how many threads a server has since a lot of the times, devs dont use the scheduler

#

¯_(ツ)_/¯

graceful hedge
#

myea

#

well cached thread pool doesnt cache threads for that long of a time tho

#

with the default factory method at least

dusky harness
#

oh, does it "return" threads back to the os (?)?

#

o

neat pierBOT
#
📋 Your paste: tiltedoomer
https://paste.helpch.at/wevugoyake

A member of staff has requested I move your message to a paste,
Most likely because it contains a config/error/code snippet.

pulsar ferry
#

Suddenly a wall of text

dusky harness
#

can you show like a screenshot of what it currently does and explain what it should do?

#

alr

#

?imgur

neat pierBOT
#
FAQ Answer:

You won't be able to upload images here directly to avoid spam, so please use https://imgur.com/upload to upload images/screenshots.
You can also use a screenshot service like gyazo or jinx and post those links here.

dusky harness
worn jasper
dusky harness
#

hm, well you have windowWidth and windowHeight, so maybe you could use those variables to calculate the offset of where all the stuff should be

#

oh it has to adjust size too

#

hmm

dusky harness
#

you can always try to see if there's anything in spark when testing your plugin

dusky harness
#

WOA

#

wow

dusky harness
graceful hedge
#

ooo poggers

wise nest
#

part of my plugin involves forcing the player to use an end crystal item
how can i check that the player is able to place the end crystal item (like making sure that another plugin or spawn protection wont block it) before spawing the entity (i assume i have to just spawn in the entity manually)
i want it to be as if the player had just right clicked with the item

cerulean birch
#

is it at all possible to use hex colors in spigot's api without adding paper/minimessage, make with ChatColor or smthing

dusky harness
cerulean birch
spiral mural
#

ey, any github wizzards here? can't for some reason with my private access token generate a valid url that works for external access?

#

at this point I've enabled everything for the url to be accessable using the token :p

dusky harness
spiral mural
#

I just want to be able to use a resource pack in a private repo

#

so by url

#

/x/x/raw/main/pack.zip?token=x so like

dusky harness
#

¯_(ツ)_/¯

#

I've never actually used github api before, this is all from a bunch of googling, so I may be incorrect on some parts

spiral mural
#

aww guess am using dropbox. appreciate the lookup!

somber dew
#

I am trying to change the text of a TextDisplay using the .setText() method. However, anything I put into that method becomes this:

text:'{"extra":[{"text":"This is a test"}],"text":""}'
#

I can not figure out why it does this, and the documentation says nothing of this.

somber dew
#

if (command.getName().equalsIgnoreCase("createEntity")) {
            if (sender instanceof Player) {
                final Player player = (Player) sender;

                final TextDisplay textDisplay = player.getWorld().spawn(player.getLocation(), TextDisplay.class);
                textDisplay.setText("This is a test.");

                // Show a particular entity
                if (!entityHider.getMembership(player, textDisplay.getEntityId())) {
                    entityHider.setMembership(player, textDisplay.getEntityId(), true);
                }

            }
        }
leaden sinew
#

That’s weird, maybe it’s a spigot bug

somber dew
#

Great XD

dusty frost
#

I mean that just looks like Components or similar

#

guess .setText() isn't the method you use to actually display stuff, idk what TextDisplay is ngl but something like that

sterile hinge
#

pretty sure that's just how spigot's text -> component parsing works for years

#

I'm almost certain this is also the case when sending messages to players etc

somber dew
#

If I wanted a player to be able to run my command, and pass in an entity, such as a TextDisplay, how would I parse a selector (@e[type=text_display])?

sterile hinge
#

Use a command library that provides functionality for that

somber dew
#

Do you know of one? I'm pretty unfamiliar with this stuff.

sterile hinge
#

brigadier, cloud, commandapi

wooden yoke
#

Hi, how can I disable expansion tasks when the expansion is disabled or reloaded?

atomic trail
#

The parameter for an enum is only called once right? And when is that?

    public enum MiscSlot {
        FILLER(ConfigManager.getList("guis.upgrade.misc.filler.slots"),
                Utils.deserialize(ConfigManager.getConfigurationSection("guis.upgrade.misc.filler.unlocked-item.item")),
                Utils.deserialize(ConfigManager.getConfigurationSection("guis.upgrade.misc.filler.locked-item.item"))),
sterile hinge
#

yes, you pass those values to the enum constructor when the class is initialized

atomic trail
sterile hinge
#

that's the fun part, basically on first access to anything of it, but that's something that shouldn't matter in the best case

leaden sinew
atomic trail
leaden sinew
atomic trail
#

But then it would be less efficient every time I call it? I suppose it just depends on if I want it to be reloadable

leaden sinew
#

You can cache the values

atomic trail
#

How do you mean?

leaden sinew
#
class SomeCachedValues {
    private final Map<String, ItemStack> cached;

    public void load(FileConfiguration config) {
        this.cached.put(MiscSlot.FILLER.key(), config.get(MiscSclot.Filler.key());
    }
}
sterile hinge
#

you can use an EnumMap in such case

leaden sinew
#

It looks like they have mutliple keys per enum though

sterile hinge
#

you can use multiple EnumMaps in such case

leaden sinew
#

Good point

atomic trail
#

I think I'm fine with just not caching it and requiring a server restart lol

#

It's just for one server anyway

#

And performance is prioritised

leaden sinew
#

Enums really shouldn't be dynamic like that though

atomic trail
#

Why not?

leaden sinew
#

And if that's where your performance issues are, you have bigger issues

#

They're meant to be constant

sterile hinge
#

EnumMaps perform very well

atomic trail
leaden sinew
#

They aren't constant at compile time though

#

Plus there's a lot of opportunities for it to throw errors, what if the configuration section doesn't exist, or what if the item it returns is null?

proud pebble
#

weird

sterile hinge
#

lmao as I said

proud pebble
#

probs something to do with how mojang made it

wise nest
#

in a plugin, if you perform an action which would call a listener (eg: player.attack could call entitydmagebyentity), will all the listeners be ran immediately, or will they be ran after the block of code (method) from the first plugin

i know that with callevent() it runs immediately but i dont know about this

sterile hinge
#

that depends on how it is implemented

wise nest
#

it is just a normal bukkit api method

hoary scarab
#

They would run after your call, unless you ran it in a scheduled runnable

sterile hinge
#

some events are triggered on the next tick if some value was changed, some events are triggered directly I guess

hoary scarab
#

Ah yeap. Read his message wrong... sirywell is right.

atomic trail
#

Will this loop through the tiers in the order 1, 2, 3 etc. all the time? for (String strTier : section.getConfigurationSection("tiers").getKeys(false)

hoary scarab
#

I think getKeys() is List so it should keep order

sterile hinge
#

it returns a Set, so no guaranteed order

atomic trail
#

Ah I see, alright

hoary scarab
#

Nvm it's a set.

#

Yeah what sirywell said.

wise nest
#

why does Minecraft sometimes just decide not to send a cancel digging packet

atomic trail
sterile hinge
#

I don't think it's a performance consideration, but more of the semantics of the config basically being nested maps

#

(and keys are a set obviously)

atomic trail
#

Not too familiar with CompletableFutures yet, what could be the reason that thenAcceptAsync doesnt run?

        CompletableFuture<BePlayer> completableFuture = database.deserializePlayerData(uuid);
        LOGGER.error("CompletableFuture created");
        completableFuture.thenAcceptAsync((BePlayer data) -> {
            LOGGER.error("Inside CF");
            Bukkit.getScheduler().runTask(bossEvents, () -> {
                LOGGER.error("Inside bukkit task");
                bePlayers.put(data.getUniqueId(), data);
                LOGGER.error("Player added to bePlayers: " + data);
            });
        }, Database.EXECUTOR);

Only the first debug is printed

#

Is it because the data is null / void?

dense drift
#

It should run after the deserializePlayerData returns a value. There might be an error thrown, try to use the method that gives you the error too (Throwable)

atomic trail
#

sneakyThrow is just this

    /**
     * Sneakily throw a checked exception as an unchecked exception
     *
     * @param e   the exception to throw
     * @param <E> the type of the exception
     * @throws E the exception
     */
    public static <E extends Throwable> void sneakyThrow(Throwable e) throws E {
        //noinspection unchecked
        throw (E) e;
    }
#

@icy shadow Sorry for the ping if it's annoying, but since it's your code you probably know why?

#

Or well the function system is at least

icy shadow
#

CF's swallow exceptions

#

if one gets thrown then thenAcceptAsync wont run

atomic trail
icy shadow
#

thats what .exceptionally or .whenComplete is for

atomic trail
#

Ohhh I see, so this would be correct usage it seems

        CompletableFuture<BePlayer> completableFuture = database.deserializePlayerData(uuid);
        completableFuture.thenAcceptAsync((BePlayer data) -> {
            Bukkit.getScheduler().runTask(bossEvents, () -> {
                bePlayers.put(data.getUniqueId(), data);
            });
        }, Database.EXECUTOR).whenCompleteAsync((input, exception) -> {
            if(exception != null) {
                LOGGER.error("Error when loading player data", exception);
            }
        }, Database.EXECUTOR);
dense drift
#

ig you can simply schedule the bukkit task on whenComplete ?

atomic trail
#

What's the difference between thenAccept and whenComplete then? Other than the exception ofc

minor summit
#

thenAccept(Async) only runs when the future completed successfully, if it completed abnormally (threw an exception) the ex is passed down the chain, so you can have a whenComplete/exceptionally further down that handles it
whenComplete(Async) runs when the future completes either successfully or abnormally, and it gives you the two potential results (value or error)

icy shadow
#

thenAccept is analogous to Optional#map, whereas whenComplete is more like Optional#orElse

minor summit
#

ifPresentOrElse

icy shadow
#

oh yeah i forgot thats a thing

#

yeah

#

also i dont think you really need to be doing .xAsync over and over again

atomic trail
atomic trail
#

Any way I can test which thread it's currently running on?

icy shadow
#

Thread.currentThread()

#

but i gave an example of this a while ago

#

unless you explicitly override it, all the tasks will run on the thread that calls .complete() or equivalent

atomic trail
#

Ty

#

(Ignore the async) Do I call the executor twice in this?

icy shadow
#

maybe

#

i mean

#

it certainly sends 2 tasks, yeah

#

it's not like it can magically compress them into 1 task

atomic trail
#

Wait if I call whenCompleteAsync I cannot use the executor, so it either doesn't register the task or it's just one long task? Or I'm just completely wrong lol

icy shadow
#

you dont need the second parameter if you're not using *Async

icy shadow
#

so yeah effectively it is 1 long task

atomic trail
#

I thought I should call async on the first "task", or is it on the last, so in this example whenCompleteAsync?

#

Not sure what .complete would be

icy shadow
#

neither is necessary

icy shadow
#

just use supplyAsync with the param and u dont need any extra asyncs

#

unless you want to switch threads

#

which is unlikely

wise nest
#

why does minecraft sometimes decide not to send a cancel digging packet?

atomic trail
atomic trail
icy shadow
#

what is openConnection doing

#

i mean really it doesnt matter

#

but earlier is probably better

#

and you only really need to do it once

atomic trail
#

Creating a new connection

icy shadow
#

well obviously lol

#

um

tardy cosmos
#

I sure hope it is

icy shadow
#

why not just use supplyAsync here

tardy cosmos
#

this looks like a perfect candidate for supplyAsync

icy shadow
#

also i think that approach to handling exceptions is a bad idea

#

all that's gonna end up happening is your futures will randomly never complete, and all you get is a single logger message to diagnose

#

ALSO you are leaking connection objects by never closing them

tardy cosmos
#

Any recommendations for a small dependency injection framework for plugins (Java)? Looking at Feather right now basically cuz it's small. I haven't used DI frameworks in plugins before

dusty frost
#

Idk how small Dagger is but I quite enjoyed it when I used it

icy shadow
#

having to explicitly bind everything with dagger got kinda tedious in my experience

#

guice is very nice

tardy cosmos
#

I've used Dagger, I'm not a fan of how the project has to be compiled once for it to resolve though

atomic trail
#

Oh wait I dont need complete

icy shadow
#

yeah uh

atomic trail
#

Just this return DriverManager.getConnection("jdbc:sqlite:" + BossEvents.DATABASE_PATH);

icy shadow
#

stop doing the RuntimeException without params 😠 that gives ZERO useful information

leaden sinew
atomic trail
#

But it's better than returning null and I print it in the logger anyway? Lol

#

I've just had issues with printing it in the exception itself

icy shadow
#

honestly im not a fan of all the manual logging

tardy cosmos
#

that runtime exception black hole scares me

atomic trail
tardy cosmos
#

Rethrowing it like that is fine just make sure the original exception can still bubble up or handle the future exceptionally

icy shadow
#

ideally you would not do any explicit try-catches, and then just log/handle any exceptions at the end in whenComplete

tardy cosmos
#

exceptions being eaten by the void is scary

atomic trail
#

Or cause I suppose it would be

leaden sinew
tardy cosmos
#

hiding errors is not getting rid of the errors AA_Dizzy

icy shadow
# atomic trail Why not?

idk really this might just be me being opinionated. it's just far nicer if you can skip all the imperative jargon and then only do 1 error handling thing at the end

#

maybe thats too functional-pilled though

leaden sinew
#

If you can’t see it, it isn’t there

tardy cosmos
#

everyone should take the functional pill

icy shadow
atomic trail
icy shadow
#

i think the thing to consider

#

is that messages in exceptions are usually pretty clear already

atomic trail
#

Yeah true

icy shadow
#

you can do debug logging but is "Connection to database could not open" really adding much when the error will already be like SQLException: Connection Failed to <address>, timed out or something, plus a stacktrace?

atomic trail
#

But in this example, I wouldn't think right off that it would be because the connection couldn't open I think

icy shadow
#

what example?

atomic trail
icy shadow
#

i mean probably

tardy cosmos
icy shadow
#

it's not like it'll just be a random exception

#

theyre usually somewhat informative

atomic trail
atomic trail
tardy cosmos
#

that's better

icy shadow
#

mostly

atomic trail
#

Debug is removed

tardy cosmos
#

it should at least work now

icy shadow
#

the catch-all Exception is possibly a bit of a smell but

#

yeah

tardy cosmos
#

can I have you change that to only catching an SQL exception for my sanity

icy shadow
atomic trail
#

Effectively

tardy cosmos
#

well yes but actually no

#

that could catch a divide by 0

atomic trail
#

Yes, but there is no divide by 0 in there, so it would just print the same exception anyway

icy shadow
#

how do you know

tardy cosmos
#

there still could be in getConnection and you'd never know

icy shadow
#

getConnection could throw anything

#

but

atomic trail
#

Because of this, so I'd also have to add the second one

atomic trail
icy shadow
#

it will?

#

it'll still get thrown even if you dont catch it

#

the CF mechanism will handle it, and you'll get to gracefully handle it at the end of the pipeline

tardy cosmos
#

it would throw it and not get caught cause you don't generally want to catch a divide by 0

atomic trail
icy shadow
#

i mean yes and no

tardy cosmos
#

It would also handle it with Exception e yes, it would just also handle cases other than SQL exceptions

icy shadow
#

sure, you'll rethrow it as RuntimeException yeah

tardy cosmos
#

You generally only want to catch exceptions you're expecting

icy shadow
#

^^ that's the real point

tardy cosmos
#

Putting a catch all for Exception is a bit dangerous in that sense

#

Catching SQLException would handle all exceptions the getConnection method is expected to throw

atomic trail
#

So the errors in the end should really be unexpected exceptions kind of?

#

Idk if that makes sense

tardy cosmos
#

you can think of it like that I guess

icy shadow
#

i mean arent all exceptions unexpected?

#

but yeah

tardy cosmos
#

some are slightly more expected than others lol

atomic trail
tardy cosmos
#

do whatever your heart desires

atomic trail
#

Nonono, that's horrible coding advice for me lol

#

Okay one last thing, then I'll leave you guys alone

#

How should the connection be closed properly in the end?

icy shadow
#

i would do that in query after everything else

#

in fact i vaguely remember the code i sent for query including the code to close the connection lol

#

but maybe im tripping

#

you can just put it in the twr and all should work

atomic trail
#

twr?

atomic trail
tardy cosmos
atomic trail
#

But wouldn't it close before I can use it then? If it's before the return in openConnection method?

icy shadow
#

far from it

icy shadow
icy shadow
#

which runs after the return

atomic trail
#

Lol sorry

tardy cosmos
#

at least it's only catching the SQL exception lel

atomic trail
#

True true

atomic trail
icy shadow
#

and theyre not using CF so you dont have many other options 🤓

tardy cosmos
#

yeah my lack of CF here doesn't help you at all

#

you could add another stage at the end that closes the connection I guess, I don't know if there's a way to do that from inside the getConnection call though

atomic trail
#

twr like this I assume?

            try(Connection connection = DriverManager.getConnection("jdbc:sqlite:" + BossEvents.DATABASE_PATH)) {
                return connection;
            } catch(SQLException e) {
                throw new RuntimeException(e);
            }
icy shadow
#

um no

tardy cosmos
#

now that one would close the connection immediately

atomic trail
icy shadow
#

there is no way to do it inside getConnection without heavy refactors, yeah

atomic trail
#

Ohhh in query yeah... Sorry missread

#

The connection is in the cf though so not sure how twr would be used?

#

Nooo

icy shadow
#

same way you're already using it

atomic trail
#

ignore the exception

#

You didnt see anything

#

It isn't this is it? lol

tardy cosmos
#

openConnection thenApply thenRun and close it in there

#

Err maybe not

#

the thenApplyAsync is gonna eat the connection

#

add a finally block to the try/catch there and do connection.close()

atomic trail
#

It warned for throwing exception inside finally so I just did this, correct?

icy shadow
#

definitely at least log something on the error

#

closing might fail

atomic trail
#

Why not throw an exception though?

icy shadow
#

good question

#

that works too ig

atomic trail
#

IntelliJ just warned me about it for some reason

tardy cosmos
#

The newest version of intellij warns you if you simply do e.printStackTrace() at all lol

#

which I guess yeah they're right it should be logged elsewhere

icy shadow
#

oh wait inside finally

minor summit
#

suppressed exceptions moment

icy shadow
#

i would just

tardy cosmos
#

the one inside finally can still fail but just throw a wrapped runtime exception there I think

icy shadow
#
try( blah; Connection newConnection = connection) {
  do all
}```
#

no need for finally at all

tardy cosmos
#

oh does that just work

icy shadow
#

think so

tardy cosmos
#

I wasn't aware you didn't have to do the actual assignment in a twr

icy shadow
#

that is assignment

tardy cosmos
#

I guess,,

atomic trail
#

I mean no errors when compiling at least

minor summit
minor summit
#

sucks

tardy cosmos
#

java 8 sick

#

java 21 gang

minor summit
#

t r u e

#

i need to update

tardy cosmos
#

I don't think it's out yet

#

a couple days I think?

minor summit
#

it released today

tardy cosmos
#

oh shit

atomic trail
#

Yes, the server runs java 8 so

minor summit
#

temurin isn't built tho

tardy cosmos
#

heck

#

do we think 1.21 will use Java 21 evil

icy shadow
#

unlikely

#

LTS moment

tardy cosmos
#

21 is lts

icy shadow
#

ik but so is 17

#

is the point

minor summit
#

so is 8

tardy cosmos
#

tru

#

oops, all LTS

atomic trail
#

This is why I don't use the exceptions in there, no idea why but it just doesnt print

minor summit
#

in there you are meant to handle the exception

#

not rethrow it, that will cause the CF to pass it further down the chain

#

log it in the logger, there's probably some Logger#error variant that takes an exception

atomic trail
minor summit
#

no

atomic trail
minor summit
#

do not rethrow it in whenComplete

#

handle it

#

if your way of handling it is logging it, use the logger

atomic trail
#

Why would throwing it not print it though?

#

I thought they always did