#development
1 messages · Page 63 of 1
Gotcha, is any faster than the other for checking if the uuid is in the table?
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");
}
}
That makes sense, ty
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
That speed difference does not matter
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);
}
fetchSize is not what you think
oh
fetchSize is the amount of records will be fetched by jdbc
in (very) short, it's generally related to memory management
well, in his case, probably no, it won't matter
but I wouldn't use it in my work database with hundreds of millions of rows
if you want to do an upsert i think you can do that in 1 query
well, yes, he can
@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)
I got this to setup default data so not sure if that would work
if(!rs.next()) {
// Data doesn't exist
createDefaultData(uuid);
}
I'm getting the error java.sql.SQLException: ResultSet closed for this but not sure how?
It's from the bottom line
you are still trying to access the balance even if the result set is empty
This should create the data though https://paste.helpch.at/egikafahil.java
If it's empty
Ah the resultset isnt updated
bingo
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();
}
🤨
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();
i suspect you're not closing your connections in some way
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
Idk about connection tho
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);
}
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
Not sure how AutoClosables work
But sounds ideal yeah lol
theyre things can be closed with try-with-resources
but also yeah, i think you can get away with keeping the connection open long-term
as long as the other two are closed
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
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
- dont close it in that method and just remember to close it whenver you use it elsewhere
- have this method take a lambda of what to do with the statement, then just return void
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
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
I am not sure what you mean by CheckedConsumer
Just CompletableFuture as well?
Or CheckedFunction
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
"Standard ones"? As in PreparedStatement and ResultSet?
Ohhhh lol ofc...
but you will get annoying exceptions if you do that
I'll create replacements now yeah
https://github.com/knightzmc/mittenlib/tree/master/core/src/main/java/me/bristermitten/mittenlib/util/lambda feel free to copy these verbatim as long as you credit me :))
although they come with a bit of baggage
Can't really see why this is better, how does it handle exceptions?
This for example https://github.com/knightzmc/mittenlib/blob/master/core/src/main/java/me/bristermitten/mittenlib/util/lambda/SafeFunction.java
by throws Exception;
it just means your lambdas can throw exceptions
it doesnt exactly "handle" them
thats gross
Any way to use this without Errors class?
And Result
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
But I can remove the applyCatching method?
yes
Gotcha, what is it actually used for?
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
Very nice, is it possible to use statement inside resultSet? This explains it more ig
The exception handling is actually so nice to have
Ohhh yeah I'm dumb lol, mb
I can't figure out what this CompletableFuture.of() is?
Okay I really don't understand what you did for the query method lol
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
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);
}
Oh yeah lol, forgot that
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
}
);
Ah okay, ty
Wdym?
Well if the resultSet is empty I want to create the default data
Hence this
if(!resultSet.next()) {
// Data doesn't exist
createDefaultData(uuid);
}
what exactly is createDefaultData doing?
Because as far as I know resultsets are immutable
Oh wait this is wrong lol, but this is createDefaultData
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);
okay… so what do you want that method to return, if anything
Before I changed query
I just want to set a "template" for the players data basically
Probably yeah lol
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
I'm doing it in 2 right now
This is what I currently use for createDefaultData and deserializePlayerData https://paste.helpch.at/exogesokid.java
I think I'm just doing it twice, but not too sure
Also a bit confused what to return in the resultSet
that does seem like ur doing the same thing twice yeah
this is a bit of a gotcha
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
Ahhh yeah that makes more sense to do, thanks.
And yeah thanks for all the help, it's beginning to make more sense lol
monadding? But yeah definitely lol
I'd recommend you to check #1147714001082523648 or #1147713999694209104
Okay i just didnt see it sorry im dumb 🤧
No worries lol
Thanks man
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
Unique attributes? A key (i forgor the name)
uh, help? Using morphia https://paste.helpch.at/goqehuxane.rb
primary key?
Is that class in your jar?
yes
not even the forums are helping 💀
if you're still getting the error then feel free to share a github repo that has this issue 🥲
¯_(ツ)_/¯
and if the project isn't public then u can make a minimal reproducible example
Yeah its not in github, will try to recreate it and will send it here tmr
You probably need to specify the class loader as ur plugins one
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.
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
Whats the easiest way to check the char per block? (Incase it ever changes)
wdym check char per block
like comparing two 32bit blocks to see if they are the same?
I think I read your message wrong... It should always be 8 chars per block correct?
yeah probably
👍 and it worked. Thank you.
I doubt it ever will.
yeah realistically it's fine
If I use something other then bitcoins block chain it might.
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)));
}
CF is an example of a "monad" which is an abstraction you see a lot in functional programming languages. im delusional in trying to functional-pill as many people as possible
Ah yeah didn't know that was a thing, but shouldn't Player in this case be private, because there's a getter and it's final? That's just what I've learned at least
It should be effectively the same, but still
Ahhh I see lmao, I'm monadding in no time then lol
it should be yeah but some of ACF is weird and old
we shall see, if you're using Streams, Optionals, or CF's in java you're partly there already
I'm not really using Streams or Optionals I think haha
☹️
no streams no optionals? L

that.... is a good point, will check if it's that.
can't find anything about setting the class loader though
Classic
is that the one method present in the Thread class?
should I set it, initialize it and then go back to the old class?
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?
just use the current thread
but you probably need to do it before, because it needs to happen before the mapper is created
this worked lol, but now I am getting this: https://paste.helpch.at/aqahifosec.md
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.
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
They dont have one I believe
Yes please
what Entity interface have the getEquipement method?
ah LivingEntity
No, Mob
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
I think Mob is implementing LivingEntity
it is
yea
I would send a screenshot if I could 💀
you get image perms once you hit tier 2
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
It is listed
I'm looking here: https://jd.papermc.io/paper/1.20/org/bukkit/entity/Mob.html
:/
bad paper docs 🤷♂️
it's kinda stoopid
what the hell did paper do to break their javadocs
https://i.imgur.com/BgqXWDl.jpeg
says the description is copy but doesn't list it
Yup
I didn't look there
cuz I was lazy
very weird
tbh I'm kinda interested too
tho why do they still use the jdk 8 style javadocs?
hm
I swear I saw some new fancy javadocs site somewhere
can't find it now tho
apparently I was just hallucinating 💀
maybe you're just designing the new javadocs in your dreams
perhaps
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)
why not Map<ChunkKey, Section[]>
A map with 3 other maps looks like a bad design D:
Maybe not exactly helpful, but try using pakkit
but yeah
what's the XY here?
triple map nesting
this would better not appear in my job during code review
you would have some shit to fix
But they are HashMaps, the code has to be very fast!!
Just wondering, even though this seems like a horrible design and XYproblem, wouldn't it be better to just have Map<Short,Material>[][][]?
And then just access it using something[chunkx][chunkz][sectiony]
but that's cursed either

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?
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
nothing, the method is overridden in the mob interface to change the nullability annotation
Why not use Table
maps are O(1), 1 * 3 is still O(1) !!! big speed, fast!
are you by any chance saving player objects in player manager?
if so, try saving uuids or names
It should do that automatically? That’s the whole point of CommandHelp
if your wondering what im doing, im making clientside private player mines
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
💀
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
doesnt grim have simulated per-player worlds or something, sounds like it might sort of work with your system
im sure ill get to that as development continues
probably
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
It only shows subcommands for a commandalias, so it just shows nothing with that
Not really, I'm just saving the BePlayer instance which contains the uuid and player
yeah but if you relog, your player object has different id and the manager class can't find it anymore (yes/no?)
How would I compare hash and target in java?
Python I can just do hash < target
Example;```
Hash: cdd88b5d653fd2eb19e773cca822d080a118f1d543e4a03f4277f43f6c4bcfbf
Target: 0000000000000000000532ae0000000000000000000000000000000000000000
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?
Yeah it is
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
almost definitely the latter
ok, thanks
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
bitcoin stuff
Decrypting the blockchain
xD
^^^
Still haven't solved this issue, if anyone has any ideas... 👀
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.
I don't care about making money with it. Just want to do it
Isn't this singleton and could be made static? https://paste.helpch.at/agifibemow.typescript
minecraft plugin bitcoin miner 🤯
people can finally make money from their vanilla survival servers averaging 0.03 active players
also plugin.getClassLoader()
wait a minute
isn't that creating a new database connection every request?
Note: I have barely used SQL, but I thought you are supposed to keep a database connection open and reuse it
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
oh ok
It's because the database is local and not remote I believe
but ig it could be static ye
Just because it could doesn't mean it should
tru
Wait yeah making it static wouldnt even fix my original issue
oh that makes sense
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.
Is this not true for plugin development? https://touchlabblog.tumblr.com/post/24474398246/android-sqlite-locking
but where do I set those MapperOptions, can't find anything....
or wait
I have kinda said this a few times lol
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
does #containsAtLeast() check offhand too? or only inventory?
It checks getContents so if that does, yes
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
U sure?
Are you in creative?
nop survival
Should not happen in survival
Which version?
I still think it's a ghost item
but it stays there
Most likely a ghost item, but even then if you're in survival it should get removed
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
isnt durability calculated client side as well
unsure, but either way an item shouldn't function if it's a ghost item, no?
ye it wouldnt
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
i think this is whats happening:
- u put the item in the slot
- u picked it out of the slot
- the server receives the #1 and puts it back in your cursor
- the server receives the #2 and puts it back into the inventory
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
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
Assert 
Why do you have assert?
purely for testing, those items are being fetched from a config file on start
lol
(but they are not null for context)
Assert doesn’t do anything
Btw highest priority is the lowest lol
IDE likes those, I dislike yellow errors lol
Bukkit's naming is dumb, highest importance runs last, it's not really priority (imo) so lowest runs first
Probably not
Probably not, just letting you know
What happens if you just cancel slot 40 and don’t check items?
but yeah, quite useful to know that lol
let me test that
yeah velocity gets this right with their PostOrder
except i think it should be an int and PostOrder a utility class
lol
@EventHandler(priority = EventPriority.LOWEST)
public void onClick(InventoryClickEvent e) {
if (e.getSlot() != 40) return;
e.setCancelled(true);
}
it still bypasses it
are you able to keep track of what is supposed to be in the offhand?
wdym?
at this point nothing is supposed to be in it, since I cancel all clicks in the slot
What are you doing btw? Just open a custom inventory and spam it?
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
or like sparky said
Is it a localhost?
Also maybe try https://hub.spigotmc.org/javadocs/spigot/org/bukkit/event/player/PlayerItemHeldEvent.html
declaration: package: org.bukkit.event.player, class: PlayerItemHeldEvent
Are you cancelling the drag event too?
Sometimes when you click, you actually drag the item
no.
I am not, will check that.
not about held item
anyone here know how to port rendering code from fabric 1.19.4 to 1.20.1
So I should preferably just have one connection for writing? And multiple for reading?
You can probably get away with just one connection if you’re not doing loads of concurrent reads
But it is possible to use more than one connection? How?
By checking if anything is already writing before doing anything?
seems like this did the trick
yeah you could do this with a lock on a connection, either just with a synchronized block or a proper lock object
I think
I am far from an expert on concurrency so this might be a terrible idea
Just creating one connection for writing I think lol
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
And this is what locks are for
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
Oh, but I should still use a seperate connection for writing though right? To not check the lock when reading
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);
}
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
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
It's to fix this
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
I prefer having to just use one executor though. Because when saving I want to check if tasks are still running etc.
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
How would that work? Not sure what you mean
Yeah, perfect
private final Object writeLock = new Object();
private Connection writeConnection = ...
void writeSomething() {
synchronized(writeLock) {
// do shit
}
}
something like this
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?
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);
}
I mean
That’ll work but
When I wrote Object I literally meant that
You don’t need a lock object AND synchronized
How can it just use an empty object though? Does it just store the information in that object?
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
Can't I just use this then? Or well I guess I can, but would that make it slower?
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
Ahhh yeah I will just do it on Object lol
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
That’s weird, you’d think it would be a pretty trivial change
Virtual threads? And I assume the monitor is what's checking if the lock is locked or unlocked in the object?
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
good point, but that's still an issue in the current code
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);
}
I think it comes down to monitors being managed by the runtime/native code while virtual threads are largely managed by java code
i dont really like having a global CF variable
ah yeah that makes sense. surely you could expose some API for managing native monitors though, right?
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);
I need a global connection though? I guess I could make a new cf for the global connect every time though yeah
yeah uh
I think they want to explore ways to basically get rid of monitors/their current implementation, but that will take some time I guess
Sooo essentially I shouldn't use the monitors then and just use a lock? Lol
the way i see it you have 2 options:
- make it global and just .get() it once, as long as you know the delay will only be on startup
- make a new CF every time and lazily initialize some global variable
i think something like this will work fine but it's a bit of a PITA to do everywhere #development message
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
Only using the locking for the update method so I think it's alright without refactoring it
good point
lazily initialize some global variable? Not sure what this means
What does the waits in get actually mean? Does it block the thread it's on until it's completed?
Ah I see, I think I'll use the first solution though, seems more simple
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
yes
theres nothing unsafe about the synchronized locks lol
but suit yourself
also fix RuntimeException
Wdym? Exception is printed in logger, just prefer it that way, for some reason it didnt print when having it in the throwable
Wasn't there a potential issue with the monitor? For virtual threads
Ohhhhh, I see
virtual threads dont actually exist yet
well
unless you're on a preview version of java
But this?
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
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
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
^^
any help here?
Why does it matter? It's just logging once that the client is created
it spams the console which isn't ideal
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:
- Store a previous variable for the current milliseconds
- Create a while loop that runs forever until completion
- Within that runnable, check if the time passed by subtracting previous milliseconds from current is less than 42. If so, do
continueand wait for the number of milliseconds to pass. - 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?
Holovid uses thread.sleep to make sure it plays at the correct framerate, if you wanna take a look https://github.com/Holovid/Holovid/blob/master/src/main/java/me/mattstudios/holovid/display/DisplayTask.java#L73
Haha never figured that out
Or
You could deploy a log filter
and filter it there
but that's just a workaround that could be exploited
pretty sure there is a #setDamage method for that
yep
@lyric gyro https://hub.spigotmc.org/javadocs/spigot/org/bukkit/inventory/meta/Damageable.html#setDamage(int)
declaration: package: org.bukkit.inventory.meta, interface: Damageable
Damageable#setDamage
how do i make a item show as being "enchanted" whilst not actually having any enchants
You can not, you can enchant it and then use the HIDE_ENCHANTS item flag
"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
I need some help anyone here
Just ask your question
continue working
Can you help me?))
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
what took you one hour
Lol
lmao
well the error clearly tells you the lines where to look at
Also don't use spigot
why isnt spigot like deprecated or smth?
Because it is not the case? Paper still depends on it and it cant just be shut down
Same reason bukkit isn't. People are keeping it updated and just making forks of it.
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
I still see questions about it from time to time
because people don't understand that they use spigot
i use spigot?
maybe you do
cool
I still use spigot 😉
good for you
imagine
quick question, if I am using JDA in an mc plugin, would it be wise to execute jda code async? (using bukkit's scheduler)
yes
no
well
if you need the scheduler then sure
but JDA already has its own async system that you can use
isnt jda async anyway with the queue shit?
yea
why you put yes if you know about it then 😐
because you can use jda async
they meant handling async themselves using bukkit scheduler
eh not really
they said
using bukkit's scheduler
just said if they can execute code async there
not to replace the queue with complete and be done with it
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
why
since you'd have to wait for the next tick
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
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
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 :=
why is it very sensitive
also note that the bukkit scheduler also uses java executor
it might be nice for bukkit to have a regular async thread pool for plugins to share
that doesn't run on ticks
paper pr xD
lol
since if u have like 30 plugins that all manage their own async, that's a lot of threads
exactly
but bukkit doesn't have that unfortunately 🥲
if every plugin made an async executor
Async tasks already use a thread pool
ik I meant that it'd be nice if there was like runAsyncTaskImmediately
since currently, you can only run it on the tick loop
¯_(ツ)_/¯
Well minecraft is tick based so it makes sense that async tasks would also be tick based
true but with the amount of plugins that a server can have, I wonder how many threads a server has 🤔
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
think it could be as simple as just exposing the cached thread pool through bukkit scheduler :>
like with getMainThreadExecutor
ye this is what i mean
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
¯_(ツ)_/¯
myea
well cached thread pool doesnt cache threads for that long of a time tho
with the default factory method at least
A member of staff has requested I move your message to a paste,
Most likely because it contains a config/error/code snippet.
Suddenly a wall of text
can you show like a screenshot of what it currently does and explain what it should do?
alr
?imgur
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.
yeah, but all the thing that don't have #queue(), are those unable to be executed async or don't have any impact on performance?
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
likely don't have any impact
you can always try to see if there's anything in spark when testing your plugin
nevermind no need 🙃
ooo poggers
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
is it at all possible to use hex colors in spigot's api without adding paper/minimessage, make with ChatColor or smthing
yes
I think it's like §x§a§b§c§d§e§f for #abcdef
works, ty
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
how are you testing the token?
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
does github support ?token=x?
https://developer.github.com/changes/2019-11-05-deprecated-passwords-and-authorizations-api/#authenticating-using-query-parameters
Seems like it was deprecated in 2019... it might've been removed by now
https://docs.github.com/en/rest/overview/authenticating-to-the-rest-api?apiVersion=2022-11-28#about-authentication since in here they use headers
¯_(ツ)_/¯
I've never actually used github api before, this is all from a bunch of googling, so I may be incorrect on some parts
aww guess am using dropbox. appreciate the lookup!
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.
Can you send your code?
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);
}
}
}
That’s weird, maybe it’s a spigot bug
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
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
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])?
Use a command library that provides functionality for that
Do you know of one? I'm pretty unfamiliar with this stuff.
brigadier, cloud, commandapi
Hi, how can I disable expansion tasks when the expansion is disabled or reloaded?
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"))),
yes, you pass those values to the enum constructor when the class is initialized
Ummm when would an enum class be initialized though? Lol this is probably very obvious but havnt used enums much
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
If you want to support editing config values without restarting the server I would not do that
Ohhh yeah that's true, but wouldn't it be kinda inefficient to do it another way?
Perfect! Thank you
No, you should just store the path of the item in the enum, not the actual item
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
You can cache the values
How do you mean?
class SomeCachedValues {
private final Map<String, ItemStack> cached;
public void load(FileConfiguration config) {
this.cached.put(MiscSlot.FILLER.key(), config.get(MiscSclot.Filler.key());
}
}
you can use an EnumMap in such case
It looks like they have mutliple keys per enum though
you can use multiple EnumMaps in such case
Good point
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
Enums really shouldn't be dynamic like that though
Why not?
And if that's where your performance issues are, you have bigger issues
They're meant to be constant
EnumMaps perform very well
They are constant in my case though no?
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?
looks like a json representation of the text
weird
lmao as I said
id assume theres a reason why its
extra {
text:"This is a test"
}
instead of just
text:"This is a test"
probs something to do with how mojang made it
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
that depends on how it is implemented
what do you mean by this?
it is just a normal bukkit api method
They would run after your call, unless you ran it in a scheduled runnable
some events are triggered on the next tick if some value was changed, some events are triggered directly I guess
?
Ah yeap. Read his message wrong... sirywell is right.
Will this loop through the tiers in the order 1, 2, 3 etc. all the time? for (String strTier : section.getConfigurationSection("tiers").getKeys(false)
I think getKeys() is List so it should keep order
it returns a Set, so no guaranteed order
Ah I see, alright
why does Minecraft sometimes just decide not to send a cancel digging packet
Yeah, which does make the most sense performance-wise ig
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)
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?
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)
I use BM's SafeFunction, not sure if it just removes the exception?
https://paste.helpch.at/kuvegoroya.typescript
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
Are they stored anywhere so I can print them to debug?
thats what .exceptionally or .whenComplete is for
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);
ig you can simply schedule the bukkit task on whenComplete ?
What's the difference between thenAccept and whenComplete then? Other than the exception ofc
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)
thenAccept is analogous to Optional#map, whereas whenComplete is more like Optional#orElse
ifPresentOrElse
oh yeah i forgot thats a thing
yeah
also i dont think you really need to be doing .xAsync over and over again
Ahhhh gotcha, thank you for explaining! :))
Oh yeah that would make sense, I guess only the first time, and then it runs on that thread?
Any way I can test which thread it's currently running on?
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
Oh yeah mb
Ty
(Ignore the async) Do I call the executor twice in this?
maybe
i mean
it certainly sends 2 tasks, yeah
it's not like it can magically compress them into 1 task
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
you dont need the second parameter if you're not using *Async
because it's defined as this
so yeah effectively it is 1 long task
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
neither is necessary
supplyAsync i guess
just use supplyAsync with the param and u dont need any extra asyncs
unless you want to switch threads
which is unlikely
why does minecraft sometimes decide not to send a cancel digging packet?
Why that and not thenApply when making the query? Sorry for all the questions just trying to learn
First screenshot is where the cf for this is created
what is openConnection doing
i mean really it doesnt matter
but earlier is probably better
and you only really need to do it once
Creating a new connection
I sure hope it is
why not just use supplyAsync here
this looks like a perfect candidate for supplyAsync
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
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
Idk how small Dagger is but I quite enjoyed it when I used it
having to explicitly bind everything with dagger got kinda tedious in my experience
guice is very nice
I've used Dagger, I'm not a fan of how the project has to be compiled once for it to resolve though
So this would be an improvement I guess? And then I close the connection when I'm done with it manually?
Oh wait I dont need complete
yeah uh
Just this return DriverManager.getConnection("jdbc:sqlite:" + BossEvents.DATABASE_PATH);
stop doing the RuntimeException without params 😠 that gives ZERO useful information
It’s a client bug, I forget the specific circumstance it happens, it may be after breaking a block
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
honestly im not a fan of all the manual logging
that runtime exception black hole scares me
Why not?
Rethrowing it like that is fine just make sure the original exception can still bubble up or handle the future exceptionally
ideally you would not do any explicit try-catches, and then just log/handle any exceptions at the end in whenComplete
exceptions being eaten by the void is scary
Ohhh yeah forgot that the cf "stores" the exception or whatever
Or cause I suppose it would be
Yeah but if there’s no exception printed, than it gets ride of the error
hiding errors is not getting rid of the errors 
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
If you can’t see it, it isn’t there
everyone should take the functional pill

Idk I feel like I save a few seconds or minutes when I debug it like that, but the exception probably explains it better yeah
i think the thing to consider
is that messages in exceptions are usually pretty clear already
Yeah true
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?
But in this example, I wouldn't think right off that it would be because the connection couldn't open I think
what example?
Oh would that be the exception? Yeah.... that explains it pretty well ll
i mean probably
why is this creating a new future twice
Yeah, I guess I can add my own if I at some point is confused, but sql explains it well I'd say
Thank you...
that's better
mostly
Debug is removed
it should at least work now
can I have you change that to only catching an SQL exception for my sanity
they're almost always informative enough. a few vague messages arent worth all the extra try-catch maintenance imo
But doesn't it do the exact same though?
Effectively
Yes, but there is no divide by 0 in there, so it would just print the same exception anyway
how do you know
there still could be in getConnection and you'd never know
Because of this, so I'd also have to add the second one
But then I'd prefer it to also throw the divide by 0 exception imo
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
it would throw it and not get caught cause you don't generally want to catch a divide by 0
But wouldn't it also handle it if I catch it with just Exception e? Sorry if I'm sounding like I'm trying to discuss, just trying to understand
i mean yes and no
It would also handle it with Exception e yes, it would just also handle cases other than SQL exceptions
sure, you'll rethrow it as RuntimeException yeah
You generally only want to catch exceptions you're expecting
^^ that's the real point
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
So the errors in the end should really be unexpected exceptions kind of?
Idk if that makes sense
you can think of it like that I guess
some are slightly more expected than others lol
Yeah not sure how else to explain it hahah
do whatever your heart desires
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?
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
twr?
I don't think it did but probably lol
I personally have my connections auto close by requiring them to be used in a lambda
https://github.com/Rosewood-Development/RoseGarden/blob/master/src/main/java/dev/rosewood/rosegarden/database/MySQLConnector.java
But wouldn't it close before I can use it then? If it's before the return in openConnection method?
far from it
thats kinda like we're going for too
Not functional coding!
Lol sorry
at least it's only catching the SQL exception lel
True true
Ohhh yeah smart
and theyre not using CF so you dont have many other options 🤓
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
twr like this I assume?
try(Connection connection = DriverManager.getConnection("jdbc:sqlite:" + BossEvents.DATABASE_PATH)) {
return connection;
} catch(SQLException e) {
throw new RuntimeException(e);
}
um no
now that one would close the connection immediately
I thought that was what the twr would do
there is no way to do it inside getConnection without heavy refactors, yeah
Ohhh in query yeah... Sorry missread
The connection is in the cf though so not sure how twr would be used?
Nooo
same way you're already using it
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()
It warned for throwing exception inside finally so I just did this, correct?
Why not throw an exception though?
IntelliJ just warned me about it for some reason
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
oh wait inside finally
suppressed exceptions moment
i would just
the one inside finally can still fail but just throw a wrapped runtime exception there I think
try( blah; Connection newConnection = connection) {
do all
}```
no need for finally at all
oh does that just work
think so
I wasn't aware you didn't have to do the actual assignment in a twr
that is assignment
I guess,,
I mean no errors when compiling at least
you don't even need assignment, you can just do
try (connection) {
// ...
}
Not in java 8...
sucks
it released today
oh shit
Yes, the server runs java 8 so
temurin isn't built tho
21 is lts
so is 8
This is why I don't use the exceptions in there, no idea why but it just doesnt print
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
But it should still print no?
no
There is, but got told to parse it in RuntimeException




