public static boolean sendPlayerToServer(Player player, String server) {
try (ByteArrayOutputStream b = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(b)) {
out.writeUTF("Connect");
out.writeUTF(server);
player.sendPluginMessage(instancia, "BungeeCord", b.toByteArray());
} catch (Exception e) {
instancia.getLogger().info(ChatColor.RED + "Excepción conectándose a " + server);
return false;
}
return true;
}
#[MEGA THREAD] Get Your Code Reviewed or Review Others
1 messages · Page 13 of 1
generally this is the issue people have with exceptions in languages like Java
here, if you catch a blanket Exception, if anything else besides an I/O exception were to be thrown (though unlikely) then this would preemptively catch it even though it doesn't try to handle the error
it is a naive method, which only returns false when something went wrong and warns to console even though it could've been something manageable
exceptions are the way you tell your server a fallible operation went wrong, and a boolean barely provides context as to what could potentially happen here
okok ty
you may also use nullability annotations to prevent possible NPEs there, which would happen if either argument there is null, shouldn't be the case but mistakes happen and nullability annotations allow you to prevent that
finally, you may want to check if the player is online before sending the plugin message
ah, sendPluginMessage throws IllegalArgumentException when one of the arguments is null, or the plugin is in an inconsistent state, so you may want to handle the latter as well
it isn't necessary for these methods to be bullet-proof since usually one designs systems in such a way that one preemptively tackles these kind of issues, but just giving you an idea of how many unforeseen issues can pop up in seemingly naive methods like that
Disregarding NDGApi, does this look better in terms of api semantics?
anyone want to code review my duel manager:
@Override
public void run() {
Player expiredPlayer = Bukkit.getPlayer(invite.getDuelReceiver());
if (expiredPlayer != null) {
expiredPlayer.sendMessage(ChatColor.RED + "The duel request you have been sent has expired.");
}
Player expiredPlayerSender = Bukkit.getPlayer(player);
if (expiredPlayerSender != null) {
expiredPlayerSender.sendMessage(ChatColor.RED + "The duel request you have sent has expired.");
}
invites.remove(player);
}
});
}```
Shouldn't ` invite.getDuelReceiver()` have a null check?
I dont think theres ever a case where duelReciever is null
but I could put like a NotNull annotation on the field within DuelInvite I guess
How about the player logging out whilst the invite is processing?
timeoutRunnable.runTaskLater(XinCraftPlugin.get(), 60 * 20);
I'm not sure if the current code has this
this is in DuelInvite the second class I linked
@steady shard I didn’t look at code but use guard clauses
Instead of != use the inverse and early return
IMO makes your code cleaner and easier to read
I usually would but because it’s only one liner I feel like this is cleaner
why would you not return there earlier
oh wait
I see whats going on
yeah its fine the way it is now
how are DuelInvite objects even created
you need to probably cancel the old invite as you just overwrite it in the map
idk if the task still stays alive and just cancels after some time anyways
but that would be confusing
I think it makes sense to just call removeInvite(player) before you put in the new one
same goes for the end of the runnable
instead of removing the invites map directly, use removeInvite(player)
you have a function for it, might as well use it
https://paste.md-5.net/muwaxivita.java
Speaking of invite services, this is mine... to be fair not really sure if a base interface is even needed though it used to be written like: https://paste.md-5.net/ralogodode.java. However since inviteImpl is already a record theres no need to enforce those getter methods, tbf the inviteService is probably the most helpful thing in the paste
Also keep in mind the set within the map is not thread safe (I forgot this was even a thing to watch out for lol)
Just wna add to the invite impl design,
it can be nice to have inviter and invitee uuid joined as key (commutatively), then no need to verify by validating invariants every time you query the cache
new DuelInvite
Why don’t you create a Service Interface and then implement it?
Wasn’t needed where that snippet is held
Also it’s a bit hard to define a “generic service” such as that would fit all implementation requirements
what
like this?
Nullable
public <T extends Service> Service getRawService(Class<T> serviceClass) {
if (serviceClass.equals(Service.class)) {
throw new RuntimeException("Service.class");
}
for (Service service : services) {
if (service.getClass().equals(serviceClass)) {
return service;
}
}
return null;
}
@Nullable
public <T extends Service> T getService(Class<T> serviceClass) {
Service rawService = getRawService(serviceClass);
return rawService == null ? null : serviceClass.cast(rawService);
}
I’m not using your api dawg
why would you even do this
Not even using a map
- IdentityHashMap for the Class<? extends Service> being the key and Service being the object.
- Use .get(serviceClass) with the map.
- Why the exception?
- Why the "getRawService" method, what is the point in that? Just put it all into the getService method
- Just cast the service to (T) when you retrieve the object.
- No need for the null check...
maps are there for a reason
Class#cast() is usually nicer, some IDEs complain about an unchecked exception and the cast() method hides it. Also makes the intent a bit more clear
But yeah the null check is redundant because null can be cast to anything
Can I cast null to null
Cast me to null
just put @SuppressWarnings("unchecked") on the package-info.java smh
Not really a code review, but I was wondering what features should I add that would help server owners better? My plugin is called Murder Run, and it's a spin-off of Dead by Daylight. These are the current features https://github.com/PulseBeat02/MurderRun?tab=readme-ov-file#features, and I want to add more, but I don't want to turn it into a bloatware plugin like CMI.
I was thinking of a design similar to PAPI expansions but for my plugin, where server owners could add custom "theme packs," containing custom gadgets, maps, and more. These expansion packs would be like a plugin that they would install
I'm not using you api
mana regenerating task
😭
i don't know if i did it right
i prob have a super wrong code or smth
cache the statsmap entry
do you mean like create a variable before starting the task?
double maxMana = this.statsMap.get(uuid).get(SkillsStatsEnum.MAXMANA);
double currentMana = this.statsMap.get(uuid).get(SkillsStatsEnum.CURRENTMANA).intValue();
->
var stats = this.statsMap.get(uuid);
double maxMana = stats.get(...);
alr
tbh you don’t rly need a task for this
Could just use time comparison with System timeInMillis
I've never done this before so I need feedback on my socket used for multi-server communication
handler example: https://paste.md-5.net/pitoxaduvo.java
and my code to talk to matches server to request a match:
private CompletableFuture<Integer> askMatchesServer(String mapName, int teamSize) {
return CompletableFuture.supplyAsync(() -> {
SocketClient client = new SocketClient();
try {
client.startConnection("127.0.0.1", 6667);
RequestMatchPacket packet = new RequestMatchPacket(mapName, teamSize);
int response = client.requestInt(packet);
client.stopConnection();
return response;
} catch (IOException e) {
e.printStackTrace();
}
return -1;
});
}```
what’s the differences?
Sorry for non direct screenshots, took this while at college - Does anyone know if this is okay for API design? Just trying to make it all abstracted. Just not sure on it, as I can’t call methods the same name. Would it be better if I have “Internal” interfaces like InternalEventBus, InternalSchedulers etc etc
The generic on callEvent() doesn't do anything, but that aside, it's definitely a unique approach
Is it good? Or bad?
In your humble opinion
I don't know if I like the whole Instances thing, but if you have a separate API and implementation then you need a bridge somewhere, and I guess that could be it
unrelated to your thing but why are you taking actual pictures of your screen instead of screenshots lol
I didn’t have dc installed on my laptop
do you just not wanna bother sending them up to your phone or something
meh
you can use discord on browser
just ignore why I do that LMAO
I used it there for the longest time, since installing it seemed unncessary since I always had it open beside a browser anyway
ah
I don't love this
fun
first off, nothing is bound so anything counts as an event, sounds counter-intuitive
the design looks fine for an event bus, however I don't see any advantages to it vs say the Guava EventBus
then again, this is just design-wise so there's not much one can really speak about. I don't love how it is bound to a singleton there since it may conflict with unit testing when times comes to shove but other than that it looks fine
it looks like it'd struggle at high-volume situations since call event has to look up for the event system every time an event is called
it'd be better to just tie together the event system with its bus, or if you want to maintain the current relationship have a Map<EventClass, EventSystem> instead but I don't know how well that will scale, food for thought
Yeah I did think about that, not sure about it
This is how I do it:
You have a context, which has events. A context is nearly always supplied, both in events and startup / shutdown.
Then, events can register a handler, register a handler with priority or unregister a handler, or it can fire an event.
The plugin isn't concerned in knowing on how to get the instance, that's done by everything else
A context, such as the server context, may have children, such as a world. Each context is then responsible for firing events, i.e. world.events().register(PlayerJoinEvent.class, event -> {}); would run when a player joins the world, but server.events().register(PlayerJoinEvent.class, event -> {}); would only run when a player first connects
Events are not bubbled up
Alright not minecraft related at all but roast me https://github.com/Y2Kwastaken/Plato
(don't roast how I look roast the project)
What does it look like?
well atm GUI is pretty simple haven't actually implemented any of the database stuff atm
the borders are for my visuals, and aren't actually going to be there in the end btw lol
I kinda figured by looking at the code a bit but I thought I'd ask. It looks really good!
Decided to go with a different approach. This is for my private API anyways, only being used for personal reasons
Honestly I like it
Neat
Should I have some minimal code in an interface? I.e.
public interface SomeUsefulMathStuff {
static int doStuff(int x) {
return x + 5;
}
}
why would that be an interface
Looks like a util class
yea ^
I mean, why would it not be
Because that should just be a util class
soo
public final class FastMath {
private FastMath() {
}
public static int floorToInt(double x) {
return x >= 0 ? (int) x : (int) x - 1;
}
}
Yes
Could've made them interfaces smh
you can't
You can almost
public interface SomethingException {
Exception create();
}```
Or something not so ass
I used to do this for constants
I like interfaces for utils
even though that isn’t their usecase, fairs
this is mine so far @pine grail
Working on the Mongo and Redis stuff rn
Who the fuck is drift mc
api is all the interfaces,
common is all the implementations,
spigot is for spigot shit ofc, like the SpigotEventSystem implements EventSystem<Event>
ignore it
Is that ur server u had in ur bio ages ago
that was Plexverse, but driftmc is a server I own
and used to run
concurrent players of 180 at one point
was fun
How much revenue
$15k+
Yearly?
Oh
damn
I never understood the excitement to play grinding servers and I still don't lol
was there anything unique to your prison server
Yeah it didn't get many dono's, that's why it shut down in the end, because all the money that was earned, was put back into the server, and I invested like $2k into a new realm, for it to fully flop and outsources another developer to make it easier, but they took too long, and the plugin was finished 10 minutes BEFORE the new realm released
was a pisstake
Yeah there was a new realm with gang houses, mines inside of the gang houses, outside of the gang houses there were pathways to spawn, where you could pvp, the houses had different themes and skins.
It was kinda cool, but no-one liked it as it wasn't their typical prison server
Ah
If prison players aren’t stuck in a box 24/7 they won’t play
That's what I figured out
If you search "DriftMC" on youtube, you'll find my server
The thing is - with modern prison server's. The prison community isn't used to it, so it's killing the gamemode, with all these "new" features. I need to try and re-release my server with a new core, but the same typical prison gamemode as back in the day, when it was OP
You have to ease in the changes but make them scream difference to make people join
What is the most popular game mode right now
Yep
I'd say SkyBlock
Only really because of hypixel
But on minehut, it's still Tycoon/Gen servers
That's so lame
We don’t talk about minehut
Minehut servers are something else
The ones on the top page are actually well made
Actually Survival is quite up there, but that's because of all the youtubers that own them p2w servers
But some of them just look like a bunch of plugins with little to no configuration mashed up
they usually get premades
HCF probably makes decent revenue if you have the right players
A lot of those players are just rich and spend so much
Oh ViperMC back in the day
HCF is competitive, don't see how to gain revenue off that
Actually prisons was really really popular with CosmicPrisons, making.. $15M in revenue
anything competitive will ultimately rely on cosmetics which is eh, hit or miss as it depends on the server's reputation
p2w, p2w, and more p2w
A lot of them make money off fraud so they have a ton of money
p2w is against EULA so it doesn't count 😛
I mean if it was back in the day... it kinda does
Skyblock boomed just because it's actually just MMORPG
hm? overcomplicated?
Yeah I just don’t get it myself
Minehut is so fun, if it's not made in skript and p2w no one plays it
tru :(
If this is for a Bukkit project, Bukkit has a NumberConversions class you can use instead which contains a floor() method that yields an int
It is not
Then rip in pieces
I used to own one of the largest geopol earth servers
Not sure about market now but very popular 2 years ago
Sir, this is "[MEGA THREAD] Get Your Code Reviewed or Review Others"
but but you can find where your house is and build ittttt
we was first tho
Def heard that before
Just wanted opinions on the basic usage of my API (Private), just to see if anyone else likes the usage of it, or any other opinions -
https://paste.md-5.net/varatuyuve.java
I wont be exactly sharing the backend code of it publicly.
Getting something from the profile service seems awfully verbose but if you don't mind that lgtm
Yeah pretty much the same criticism tbh
and as a matter of preference, I would prefer EventBus#fire() to be either #call() or #push(). But again, preference. If you prefer #fire(), nothing wrong with that either
Fire is bad because it might miscommunicate that the event is on fire probably not a good look for your users to be calling the fire department.
Yes. All event calls must contain phone numbers of the callbacks subscribing to that event >:((
@Slf4j
public class PlayerConnectionChecker implements PluginMessageListener {
private final ConcurrentMap<String, CompletableFuture<Boolean>> futures = new ConcurrentHashMap<>();
public CompletableFuture<Boolean> checkPlayerConnected(Player player, String targetPlayerName) {
CompletableFuture<Boolean> future = new CompletableFuture<>();
futures.put(targetPlayerName.toLowerCase(), future);
try (ByteArrayOutputStream data = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(data)) {
out.writeUTF("PlayerList");
out.writeUTF("ALL"); // Request the player list for all servers
log.info("Requesting player list from BungeeCord");
player.sendPluginMessage(XinCraftCore.get(), "BungeeCord", data.toByteArray());
} catch (Exception ex) {
future.completeExceptionally(new RuntimeException("Unexpected error while writing to byte array", ex));
}
return future;
}
@Override
public void onPluginMessageReceived(String s, Player player, byte[] bytes) {
if (!s.equals("BungeeCord")) {
return;
}
try (DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes))) {
String subChannel = in.readUTF();
if (subChannel.equals("PlayerList")) {
in.readUTF(); // Read the server name
String[] playerList = in.readUTF().split(", ");
log.info("Received player list from BungeeCord: {}", Arrays.toString(playerList));
for (String playerName : playerList) {
CompletableFuture<Boolean> future = futures.remove(playerName.toLowerCase());
if (future != null) {
log.info("Player {} is online", playerName.toLowerCase());
future.complete(true);
}
}
// Complete futures for players not found in the list
futures.forEach((playerName, future) -> future.complete(false));
futures.clear();
}
} catch (IOException ex) {
futures.forEach((playerName, future) -> future.completeExceptionally(new RuntimeException("Unexpected error while reading from byte array", ex)));
futures.clear();
}
}
}```
my class for checking player connection server wide
usage:
XinCraftCore.get().getConnectionChecker().checkPlayerConnected(fromPlayer, targetPlayerName).thenAccept(isConnected -> {
log.info("Player {} is connected: {}", targetPlayerName, isConnected);
if (isConnected) {
String message = String.join(" ", Arrays.copyOfRange(args, 1, args.length));
XinCraftCore.sendNetworkPlayerMessage(fromPlayer, targetPlayerName, message);
} else {
fromPlayer.sendMessage(ChatColor.RED + "That player is not online throughout the network!");
}
});```
this needs to be more robust
- you want to check whether the player is null or isn't online in that checkConnected method and returning early if so
- probably add a cleanup function that gets called onDisable, so if there are any pending tasks, you will just cancel them
I wonder if there is any difference between using guava's byte array data I/O or just plain old byte array I/O streams wrapped over a data I/O stream for things like these
the size is known beforehand so ultimately using a stream would be more inefficient as it is one more initialization & indirection? Would have to benchmark that
barely a difference
would probably been better to test single-shot for something like this instead of average time but eh
https://gist.github.com/JavierFlores09/02a0daefb0fbdce88d1dec3c48a5d988 here's the benchmark if anyone is interested
It has a couple warm up iterations
Though I'd like more iterations for both warmup and run
this feels useless, doesn't guava just delegate to the std stuff?
like its more of a convenience and boilerplate-reduction thing afaik
it does not
it indeed is pretty useless to measure performance of a convenience class, however I was curious of whether it performed better or not than the traditional way to do it
with more iterations that error bar will be less pronounced and the difference should be negligible, but a 1-shot time benchmark would be good to know perhaps
whats the difference if I so may ask?
cuz unless my memory is really washed ByteStreams wraps and delegate the thingy more or less
they just got their own implementation of DataInput/Output for this, unrelated to ByteArrayInput/Output and DataInput/Output streams
in theory, the convenience is that it combines both a data I/O stream and a byte array I/O stream
you also don't have to worry about closing the thing since it only works with arrays of known-size, meaning it'll get GC'd along with whatever array you pass it
yes but the implementation provided by ByteStreams#newDataInput/Output uses ByteStreams$ByteArrayDataInput/OutputStream which is just a wrapper around DataInput/OutputStream
this is how I remember it at least because I was interested in this 2 years ago
maybe they switched it up
however, if they are different, then a benchmark is interesting certainly ^^
you're right actually, it just delegates to Java's byte array I/O streams: https://github.com/google/guava/blob/master/guava/src/com/google/common/io/ByteStreams.java#L329-L330
https://github.com/google/guava/blob/master/guava/src/com/google/common/io/ByteStreams.java#L511-L513
I was under the impression it was a different implementation because I saw the private byte array data I/O stream classes and thought they were doing something differently, didn't realize it was just a wrapper
so, yeah. There's really no difference implementation wise, the guava classes are just a convenient class for this
the code for this hasn't really been touched in 10-15 years, that's crazy
Some people are running code that's older than their users lmao
I mean yes
considering that most OS devs are literally shipping code older than most of their users
Windows code from the 1990's be like
fr
That one time I reboot my laptop and accidentally end up in DOS mode
haha yea, I mean its written in the way that its very reasonable to assume a different impl, and u almost convinced me that was the case
100%, deviously so
I have rewrote it to what I talked about yesterday
?paste
XinCraftNetwork: https://paste.md-5.net/odanogapaw.cs
NetworkPlayer
@Data
public class NetworkPlayer {
@Nullable
private UUID uuid;
@Nullable
private String name;
private boolean online;
}```
public enum PlayerRequestType {
UUID,
NAME
}```
I use it like this in my message manager:
XinCraftNetwork.getPlayer(recipient).thenAccept(networkPlayer -> {
if (!networkPlayer.isOnline()) {
fromPlayer.sendMessage(ChatColor.RED + "That player isn't online!");
return;
}
Conversation conversation = new Conversation(fromPlayer.getUniqueId(), networkPlayer.getUuid());
conversations.saveConversation(conversation);
});```
- if you're going to commit the crime of using Lombok atleast use @Data annotation
what is wrong with lombok
but ty thats neat
edited for that
Hello! I am trying to make random teleport plugin and have a problem. I have made a /rtp command which starts 5 second cooldown before player teleports. After cooldown it searches for random location in world. I have a problem that sometimes when waiting time ends server lags for 1-3 seconds and MSPT rises to 300-1000. What could be here wrong? Can provide more code and would be grateful for help
private static final int MAX_ATTEMPTS = 32;
private void teleportPlayer(Player player, int radius) {
new BukkitRunnable() {
@Override
public void run() {
World world = Bukkit.getWorld("world");
if (world == null) {
player.sendMessage("Мир не найден.");
return;
}
for (int i = 0; i < MAX_ATTEMPTS; i++) {
int x = random.nextInt(radius * 2) - radius;
int z = random.nextInt(radius * 2) - radius;
int y = world.getHighestBlockYAt(x, z) + 1;
Location location = new Location(world, x, y, z);
Material blockType = location.getBlock().getRelative(0, -1, 0).getType();
if (blockType != Material.WATER && blockType != Material.LAVA) {
Chunk chunk = world.getChunkAt(location);
if (!chunk.isLoaded()) {
world.loadChunk(chunk);
}
Bukkit.getScheduler().runTask(Plugin.getInstance(), () -> {
player.teleport(location);
player.sendMessage(color("&6[\uD83D\uDD25] &fВы были успешно телепортированы на координаты: &6" + x + ", " + y + ", " + z));
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 40, 0));
player.playSound(player.getLocation(), BLOCK_NOTE_BLOCK_PLING, 1.0f, 1.0f);
player.setFallDistance(0F);
}
);
return;
}
}
player.sendMessage("Не удалось найти безопасное место для телепортации. Попробуйте ещё раз.");
}
}.runTaskAsynchronously(Plugin.getInstance());
}
You're trying to load up to MAX_ATTEMPTS chunks within one tick (unless asynch does some stuff idk)
Maybe even generate new chunks
That for sure is not good for the server.
if (!chunk.isLoaded()) {world.loadChunk(chunk);}
That is pointless I believe, just creating a Location or accessing a block there loads the chunk.
And I do not understand the need for the nested runTask.
Was trying without load chunk and still had this problem.
Maybe will remove async and chunk load also, thanks. Was just trying to check if it will help.
?workdistro
Will check, thanks.
Hey yall been awhile, took a break but back at it.
How does this look so far?
public void updatePlayerDaily(UUID uuid, Daily daily, int amount) {
PlayerData playerData = playerDataManager.getPlayerData(uuid);
Player p = Bukkit.getPlayer(uuid);
String playerKey = String.format("%s.%s", PlayerData.defaultData.DAILIES, daily);
String dailyKey = "";
int dailyAmount = 0;
int playerAmount = playerData.getInt(playerKey + ".Amount");
for (String type : dailyFile.getConfig().getKeys(false)) {
//Gets the identifiers
for (String s : dailyFile.getConfigurationSection(type).getKeys(false)) {
dailyAmount = dailyFile.getConfigurationSection(s).getInt("Amount");
dailyKey = String.format("%s.%s", type, s);
}
}
int currentAmount = playerData.getInt(playerKey);
int newAmount = currentAmount + amount;
if (dailyAmount >= playerAmount) {
send(p, "DAILY COMPLETE");
return;
}
dailyFile.getConfig().set(dailyKey, newAmount);
}```
My only cratique doesn't even involve this method. If you're going to wrap PlayerData shouldn't it be fully aware of all the data it contains instead of #getInt(playerKey + ".Amount") I feel like a data class should be aware of all its data? I just feel like their has to be a better way to present this tbh.
I don't think there's anything wrong with the class itself, but rather the name
if you named it PlayerConfigurationSection, there would've been no issues with regards to what data the class contains since it'd essentially just be a ConfigurationSection with some utility methods
but I do agree that generally, if you want to make a type, and your data is formally defined, as in, this PlayerData/PlayerConfigurationSection thingy doesn't just hold arbitrary data for the player, then there'd be better ways to express what it is holding instead of having to manually parse the data each step of the way
something me and raydan (and ebic) have been working on for a couple weeks now: a spigot plugin development toolchain - for the gradle plugin I'd like some feedback about the current buildscript structure: https://pastes.dev/wUkJa6uvmr
todo:
- plugin.yml builder
- runServer tasks and copying
Another todo is ✨ configurations ✨ (please shoot me in the head this is unbearable)
🤔 what are the patches
They allow for fast spigot jar generation
I see
Could you add an option to use a Spigot jar from maven local
or make it run BT
Probably
why exactly?
I mean, just compileOnly and mavenLocal it I guess
I wonder if we could make the minecraft configuration also support jars from anywhere, so you can get any jar remapped (although I suppose version detection will be difficult)
Yeah, but the minecraft configuration is special
pluginImplementation seems kinda useless at best dangerous at worst ngl
It's for remapping plugins that use nms + to add them to the plugins of the runServer task
It's nice for e.g. Plib, I really hate having to install it on my server separately
?
hi its me
yeah you helped us a bit
pluginImplementation's are expected to be remapped if they use nms so the user can easily navigate through the decompiled code and check what minecraft classes it is using
Then it should be included as pluginTestImplementation or something along those lines and shouldn't be included in regular builds for distribution
Why?
I don't think I should have to say that including it in regular distribution is really really dumb
It will only be included in the jar if it also uses the configuration for that , like fabrics include(modImplementation("mod"))
Enlighten me
It doesn't shade the plugin, as in, shade the jar contents
Ofc it might not look exactly like that in current test builds, it probably will in final ones
- You're distributing someone else's plugin without their permission while not always illegal, almost certainly disrespectful.
- Not sure how you handle clashes once deployed, but if you don't things will break
- Even if you do above you've now bloated your final jar by completely unnecessarily including other plugins.
for 2. I need to check versions, currently it just does not get loaded if said plugin is already installed in the server
Also, I think rad, when making pluginImplementation, had no idea what it does kek
for 3. how does it break other plugins?
If im the server owner I install your plugin than install other plugins. Let's say idk 3 or 4 of my plugins use protocol lib your plugin now just has a dormant protocol lib sitting inside it doing absolutely nothing, but taking disk space and slowing startup time because you need to do clash detection. The more of your plugins I install the worse this gets and the slower startup becomes. I'm not saying the idea is bad for testing, but when deployed it's just not sensible imho
It will probably be more of a thing for plugins specifically made to be jar-in-jar'd kek
Most servers already have lp, pp and plib, so it makes no sense to jij it
If they're meant to be done like that you can shade the library? This just seems like a worse version of shading that can let dumb stuff slide
If it's a library as plugin you still run into the issues I've said above.
It's not the worst version of shading, it's just something with a very specific use case. You can probably just JIJ all your dependencies (doesn't have to be a plugin I think) so your jar looks cleaner kek
At that point you should bootstrap then
Fabric does it, so I don't see a reason for us not to, since we are trying to mimic fabric looms functionality
In which case instead of doing what this is you should implement a proper bootstrap
But in a spigot realm
I mean "Fabric does it so it's fine" isn't really a good counter at all. I'm just gonna leave it here as I pointed out issues I see in such a system
I believe JIJ is just for some specific use cases and it's just cool
I find it to be really useful when e.g. you're the only plugin/mod on the server, i.e. fully developing the server yourself, and you don't need to worry about every environment having dependencies installed, but rather just bundle them all. Whether it should be used for distribution, that's up to the plugin developer I guess. With licensing, plugins have to be GPL because of Bukkit, which I believe (don't quote me on this) allows redistribution like JIJ. However I am in no needs of any knowledge on the topic of licensing
👋 I have been working on a skyblock remake for a while which uses Minestom. Please beware with me I started on quite a few parts of gamemode but never finished them but if anyone has any opinions x)
https://github.com/unjoinable/Skyblock
*I accept all levels of criticism provided you elaborate your point. *
Your groupid name is misleading and not good; it should not include 'java.io'
it should be com.github.unjoinable.Skyblock
I will fix it
How did you even come up with that
I will show (nvm I forgot I deleted all my screenshots) But, someone told me it is a good idea
using gradle is pretty based
no it is not
Does minestom even support maven?
also avoid a general 'Utils.java'
Disinformation; go jump from a bridge
it does
compared to your other utils classes, Utils.java is lacking in documentation
Documenting util classes is so fucking funny
also you should prefer List over []
arrays?
yes
Arrays can be quite efficient
why wouldn't it
I am surprised no one commented on my auto version updater
These should be tossed on their separate methods
good idea.
that can very well come back to bite you in the ass when you're offline or blocked by cloudflare
Lots of singletons.. hm
performance is negligible
sero clue what this comment means but this should be a discrete class. Tasks like these don't belong on your "main"
🤦♂️ fuck I forgot to remove the filthy comments
You can omit the first and last impl
duplicate code = nasty
and varargs covers every scenario
okay
/**
* Converts a map into a byte array
*
* @param map Map to convert to byte array
* @return The byte array into which the map is converted
*/
public static <K, V> byte[] convertMapToByteArray(Map<K, V> map) {...}```
Lol
I don't think closing the buffered reader closes the input stream reader too
(I forgor how generics worked for a min so had to look em up lol)
whats an Arto
StringArrayIntoIntArray
probably some unique fish
A certain sort of cheese
(probably not)
which class is that?
if-return-else smh
declaration: module: java.base, package: java.time, class: Duration
I mean I could use ternary
no I meant in my code.
Keep your constants above everything else. This is messy
Ability
Also why are messages in the player class??
Zero reason to indent the else. just if-return-return
because they are used there
poor excuse
I can shove all the ability related code into AbilityHandler
which I should have probably done b4.
This isn't scalable. If I want to introduce custom abilities I need to fork your project. Ability cost types should have their own handling for this
This looks sloppy and doesn't follow naming conventions
Can't you store these as constants? makes no sense
oh sorry I forgot to rename those
Make sure to do defensive copies. Returning mutable maps is error-prone
(check pins on #help-development for a proper explanation)
public static Statistic byOrdinal(int ordinal) {
return values()[ordinal];
}``` ⚠️ ordinal detected
ordinals are fine if you're doing networking
because ✨ bandwidth ✨
zero excuse elsewhere
I love saving bandwidth when doing networking
it's quite difficult to guarantee read-only map copies when using records
oh wait sorry i ma tired
it is okay
🤦♂️ confused records
While this is fine, interfaces with a "constants" class are preferable over enums for future expandability (look at kyori's NamedTextColor)
I don't see a reason for rewards to be capped to a type. For example, at work we make rewards pretty arbitrary
kek
We have rewards that just run a block of code that's registered somewhere
So you can have a ExpReward, CurrencyReward, ItemReward, CommandReward, StatisticReward, RunnableReward etc
some things are final while others aren't, but they could be
the usage of records vs classes is also inconsistent
I'd rather just see classes everywhere
thats planned to be removed.
Zero reason for ranks to be hardcoded
why not?
because they don't need to be hardcoded
why would they?
I never plan on adding more
okay
also close your minimessage tags
The idea is to build good habits, not to fight us or be ignorant to the principle of "things should be expandable without the need to change code"
oh no, my intentions are not to fight you. I am just trying to explain my pov.
sorry if u got the wrong message
that's fine
You can very easily make this a data object and make it configurable
And maybe even have a list of permissions and just check for those
yes
You can them have your consumer make custom ranks (like hypixel's "pig++" for example) that are just an amalgamation of certain features
Valid reasons, I will change it.
I plan to shift all of this in SkillsHandler (to be coded)
Your codebase shouldn't be constrained by "design", but rather by choice
also maybe avoid shoving huge chunks of code monolithically
you should look into splitting it up
I'd like to see a modular approach for this
oh yes, I would love an alternative to this.
minestom ECS would be sexy to see
peak code
based off the name I would not initially think a slot could contain a player
These can be converted to a List<String>
I could ig
These can be converted to a class + constants
yea, but problem is all my Stats now use ordinals
womp womp don't use ordinals then
it was to speed up stuff
wow
foreshadowing
this is how we do statistics at work
these are a little different stats
:3
Is there any reason for this?
Then they're not statistics but attributes instead
yes
Item 28
then don't call them statistics
I am not going to lie, naming in entire project is horrible
cool, you know where to improve
public event handlers smh
do it
ah yes lemme make them private and force bukkit to do even more reflection on startup
I make them private in java because it's about as long as making them public
with kotlin it's just bloat imo
and then register them at runtime too 
@Override
public @NotNull List<Component> lines() {
List<Component> lines = new ArrayList<>();
lines.add(getDateLine());
lines.add(Component.empty());
lines.add(getSeasonDateLine());
lines.add(getTimeLine());
lines.add(getLocationLine());
lines.add(Component.empty());
lines.add(getPurseLine());
lines.add(getBitsLine());
lines.add(Component.empty());
lines.add(getNetworkLine());
return lines;
}``` oof
would u take a look at items package.
Sorry, I really tried finding alternative
placeholders + configurable list of strings
Oh this just incorporates parts of https://github.com/Swofty-Developments/HypixelSkyBlock doesn't it
no
choose your next words very carefully
it is just we both were doing same thing
okay
oh that
/**
- @author Swofty
- @since 1.0.0
*/
he coded that for me
mhm
hmm okay
if by chance u need proof
No its fine, since it uses milliseconds mostly
is there a reason time starts at 1560275700000L and why this is final
the start of actual hypixel skyblock
So the time in my server exactly matches the Hypixel Skyblock time
but why can't this be configurable
that's what I'm asking
why are you hardcoding this
is SkyblockStandardTime just a timestamp
Unrelated, but I recommend coding your own scheduler manager that can relay on the server one instead of directly using the server one
skyblock time works a little different
the class still mirrors a timestamp
you can simplify this a lot to a Date wrapper
why is there both Skill and SkyblockSkill
Skill is an enum
linguistically this distinction doesn't make sense
and the Skill enum seems useless
if you then have actual Skill classes
I will convert it into fields
would u mind taking a dive in items package?
there is soo much redundent code rn.
can you explain what Registry does
just stores object in a Map with a few methods
private static final ClickableButtonRegistry INSTANCE = new ClickableButtonRegistry(); ew
why ewe
just storing all different types of GUI Buttons
nope
Then it’s fine
//logic
try {
NamespacedId id = NamespacedId.fromString(item.getTag(SkyblockInventory.BUTTON_TAG));
ClickableItem button = ClickableButtonRegistry.getInstance().get(id);
button.onClick().accept(player, event);
} catch (Exception e) {
player.sendMessage(INVALID_ACTION);
}```
it allows me to do stuff like this
need to rename the registry rq
Taking your advice head on
public Daily getDaily(UUID uuid, Daily daily) {
return this.playerData.get(uuid).getDailies().get(daily.getName());
}
public void updateDaily(UUID uuid, Daily daily, int amount) {
Player p = Bukkit.getPlayer(uuid);
Daily playerDaily = this.getDaily(uuid, daily);
int currentAmount = playerDaily.getAmount();
int newAmount = currentAmount + amount;
if (daily.getAmount() >= playerDaily.getAmount()) {
send(p, "TASK COMPLETE");
return;
}
dailyFile.getConfig().set(daily.toString(), newAmount);
}```
Registries can definitely be singletons as long as you aren’t modifying them
Otherwise, you would need to add freezing and unfreezing and stuff like that
nope they arent being modified
I feel you can better do a singleton as an enum
registries my beloved
After all, that’s the point of a registry right?
if youre using a class singleton, have a private constructor
please no
spark and LP do this a lot and I fucking hate it
istg I've seen enums extend javaplugin
Oh yeah saw this
ig it's foolproof but it's degen
Actually it’s pretty common in most Java frameworks for the enum
also I just realized
kotlin object >>>>
why is this a thing you have https://github.com/unjoinable/Skyblock/blob/main/src/main/java/io/github/unjoinable/skyblock/util/StringUtils.java#L45
enum MyPlugin extends JavaPlugin {
INSTANCE;
@Override
public void onEnable() {
...
}
}
why can you not do String.formatted
There’s an argument for it, I can’t find it
I personally don’t use this way, but there’s an argument for this
Yeah
is the plugin loader even able to load that
zero way to bypass it
String.formatted felt weird idk
yes
more weird than calling another method
In my opinion it’s too far fetched
No need to go that far
It’s just a Minecraft plugin
Who gives a fuck
but yeah MiniString is the new thing for that..I just need to replace all the existing code to use MiniString
type shit I do when some idiot locks down a plugin and has an "unfixable bug"
I've extended CraftScheduler before
y? Scheduler seems fine
reasons
I am enjoying Minestom.
is SkyblockLevel even used
nrn
it's very thin
I kinda went on a package/class making spree, when I got really bored like a week ago
for (io.github.unjoinable.skyblock.item.component.Component component : components.values()) {
if (component instanceof BasicComponent) {
((BasicComponent) component).applyEffect(this, builder);
}
}
``` this is minor but I see a lot of improvements possible
also why are you using the FQN here
FQN?
You can use a stream for that if you want to
streams are used when too many objects
these is probably at max 20 objects
Your current code is fine
Builder has a generic name but isn't broadly applicable and is a SkyblockItemBuilder
🤷♂️ what else do I name it
oh ok
public Builder() {} //default constructor for gson maybe I am deficient in GSON but why are you serializing your builder
or expecting to
a builder is not a thing you store and load
It is used for deserializing json via Gson
well duh, I read the comment
{
"material": "INK_SACK",
"name": "Toxic Arrow Poison",
"category": "ARROW_POISON",
"tier": "UNCOMMON",
"npc_sell_price": 2000,
"id": "TOXIC_ARROW_POISON",
"description": [
"§8Consumed on arrow shot",
"§7Arrows deal an additional",
"§7§210% §7of damage as poison",
"§7and reduce healing by §224%",
"§2§7over §b4 §7seconds."
]
}```
okay wait hold up
that builder only exists for gson
you need a TypeAdapter son
this is not a builder
'builder' means a specific design pattern
Rarity has ordinal and can be List
not hardcoded
If you are able to, I do recommend using JSON for the description components instead
no in the enum
why does LoreSystem store a player
theres no docs telling me what LoreSystem does
some components take player as argument to make lore
it doesn't for now.
it shouldnt ever
because then you're having a class do more than 1 thing
then you need to let those 'components' get the players another way
not by omnibussing it with LoreSystem
item category is hardcoded
does not need to be
blow that up and start over
why not
alr sure I can do that
ItemRecipe is useless
I will remove all enums.
Unrelated, but I strongly recommend you use a proper locale system
oh I made alot of packages and classes but never made stuff in them
well maybe dont do that
my enums can be removed
this project is made using Minestom
oh, sorry
I don't understand your 'components' at all though
could I have a crumb of documentation
they are just a way to make an item
group your typeadapter with their subjects
well some explanation is in order for those
I tried to follow ECS
Component interface is useless
marker interface
what implements Component
all of these (minus ComponentContainer and Component it self)
then the actual components used by items implement these
lets say name? pretty basic stuff
public record NameComponent(String name) implements BasicComponent {
@Override
public void applyEffect(@NotNull SkyblockItem item, ItemStack.@NotNull Builder builder) {
Rarity rarity = item.container().getComponent(RarityComponent.class).rarity();
Component colouredName = Component.text(name).decoration(TextDecoration.ITALIC, false);
if (rarity == Rarity.UNOBTAINABLE) builder.customName(colouredName);
builder.customName(colouredName.color(rarity.getColor()));
}
}```
BasicComponent semantically is odd
Basic tells me nothing
yeah, redo your builder and dont use a builder like this
that is not what a builder is for
and it has no real need to be static inner class
split it
the components that are only applied, but never to be retrieved since it is defined in a json file containing all the default items
so its a transformer?
AbilityCostType is no bueno
and that toString reeks regardless
ability.abilities is awkward
where is that?
oh right
well it sort of made sense
since abilities contains all different abilities
laughs in pack.pack.pack
(PackProvider#pack -> BlockBrawlPack#pack -> ResourcePack)
IslandImpl is a bad name imo
thats 1 place I tried to avoid enums
idk what else to put rn
medium rare
I know
they are to avoid Minestom from spamming console, telling me to register handlers
at the very least explain that
but I'm not sure you fixed anything.
more like slapped a silencer on it
yes
Button sucks
the item is a function?
odd
task is weird
SkyblockMenu includes locale stuff
@SuppressWarnings("all") //experimental annotations giving false warnings. this reeks
why
No Minestom has @API.Experimental for packets
also even if, suppressing all?
show me where you use that Experimental
um um sorry that is not finish
checkCollisions method
is experimental
it's not annotated as such
then suppress on that method
not the entire class
and not ALL warnings.
and you need to write out that justification.
never blindly or broadly suppress
oh okay
ThrowableStack is an armor stand?
oh its for like
save it for the docs
oh okay
write it in the documentation
alright
but do explain to me why it has a Player field
to push it with respect to player
it is used in onCollide too
then you need to reexplain what a ThrowableStack does if it can hurt people
and rethink that
okay
I made it enum at the time so that I can easily add it to command suggestions, but now I think about it yeah I should do a registry
NPC also contains locale
npc class does nothing for now
loot.loots awkward
what do u mean by this?
private static final Component NPC_MSG = StringUtils.toComponent("<yellow>[NPC] ");
this is hardcoding language
no bueno
loot looks a bit useless
DamageIndicator
yes
this rainbow effect is a little different
it wraps around the COLORS
what do u mean?
amended
yea
it doesn't have any meat as of now
Thank you, for reviewing all my code. I will definitely go over our talk and start fixing parts of code.
for a first project like this it's not terrible
but there are cracks in the foundation
but it's a decent start for sure.
👍
I spent alot of time on making that item's system, but at some point I gave up.
I have a feel it is too late to convert Statistics to fields instead of enums
Unless I add indexes as a param.
i’m going to write an advent calendar that has 24 challenges for my minigame server and each challenge has its own set of criteria
how should I write it so it’s not some massive class or 24 different classes
just have like a few different classes for different criteria such as goals or kills and them have one class registering them all?
yeah some guy wrote old quest system and legit did a class for each quest
including like

quest name 1 quest name 2 quest name 3
all different classes
for the same quest with different levels
Nah you just make a class for each criteria
and then on goal and on kill do something like adventmanager.onkill
and then it’ll handle all the kill related quests
Make a class for each objective
and then make a quest class that just holds objectives, requirements and metadata
like advancements, there are a set amount of trigger types for an advancement, and then you can combine certain ones and stuff to make the actual advancement
https://paste.md-5.net/ubaceduwaj.java
This shit is all over the place but have at it ❤️
looks fine to me 
Well shit
minigames never really look beautiful tbh
maybe I did decent job
I lied you did horrible
kek
I lied about you doing horrible you did fine
KEK
The pinkies are fighting :(
idk what a RoundState is, but why can you set it 👀
That was purps doing I had a bug with rounds not starting / ending and that was his fix
it works so
I aint gonna touch it
https://paste.md-5.net/rexosacuho.cs
(context tho)
https://paste.md-5.net/botiguxega.java
How about this? I literally have not looked at any other ecs impls and I wrote this in 5 mins so uh?
ahaha
bro
you know you can put multiple classes in a single file right
why do different levels even have different classes, that sounds like bad design to me
exactly
I hope this is not yours cuz its the opposite of what we told you to do
https://paste.md-5.net/rilucaraje.java
Whatcha guys think (The idea was base implementation for a free for all round)
yea its not its what they wrote before me for quests
finished my advent code if you guys would like to review it
im chucking it on github now
I would appreciate any and all feedback as I've never wrote a system like this before
command: https://paste.md-5.net/menuyobelu.java
listener: https://paste.md-5.net/sopibefaqa.java
if (label.equalsIgnoreCase("openinv")) { you don't need this if statement
you should use if negation more. instead of
if (sender instanceof Player player) {...}
// do
if (!(sender instanceof Player player) {
sender.sendMessage("This command can only be run by a player.");
return true;
}
this makes your code far more readable.
Use components avoid legacy text, CommandSender#spigot#sendMessage() You can use something like Minedown or Adventure + MiniMessage to make life easier, Legacy text is 11 years too old.
Don't hardcode your messages, otherwise people who don't speak english can't configure your plugin for their languages or customize it at all
if (event.getView().getTitle().startsWith("Editing ")) {
DO NOT DO THIS AT ALL NEVER EVER EVER EVER DO THIS.
Otherwise i can defeat your plugin by renaming a chest "Editing ". See this thread
Your entire GUI system is just kinda bad in general the above thread will help with that as well.
Not sure if I vibe with criteria being aware of your database. They probably shouldn't be, not to mention doing a database call that often is uhhh horrible at best.
public EatCriteria withAmount(MongoClient client, int amount) {
this.amount = amount;
this.collection = client.getDatabase("advent-2024").getCollection(getName());
return this;
}
Database logic and in memory criteria should be completely separate
I see you mixing this logic all over your criteria
you should be batching database calls together instead of firing them off right as they happen
should probably have an onMatchComplete type deal that sends off all the stats
you repeated my first two points smh smh
also legacy text is officially supported on spigot still, so I wouldn't say it is bad to use it
that's because choco is slacking smh
its updated
then he should force md to merge it
what does Ctrl + Q do in discord, I accidentally pressed it and closed my legcord instance lol
press ctrl alt shift W
I thought it just crashed because of a missing feature in legcord or something
what the hell is legcord
previously armcord, they had to rebrand due to Arm being a bitch about their trademark
how can you trademark the word "arm"
lol
wtf
the same way you can trademark apple
Gonna trademark penis 💯
it depends on the business, if it is tech-related then you're probably going to have a talk with apple lawyers
the thing I wanted to achieve though was that it would tell the player that they completed the challenge as soon as they did, for example as soon as the game starts it would notify the player they completed it
my idea was that it’s called async so it should be fine
In memory tracking my friend
Pull, cache, wait, bulk update
Your system doesn't have unlimited threads even async calls can add up
Any advice on this game round queueing system?
Tbh doesnt look much like a queue to me
okay thank u
To be fair I haven’t done any matchmaking stuff yet either
I don't think your QueueService needs to exist. Pretty much all of that can live within QueueManager, no?
Also, maybe you should be introduced to a Queue object 👀 I feel that would be useful
Probably however I was tired and just trying to use as many objects as possible kek
(Not the same but first link I sent)
Yeah definitely not the same. A Queue is just a FIFO/FILO Collection
FIFO is more like a proper queue like... a real life line. First in, first out. FILO is more like a stack of objects. Useful in its own right, but FIFO is probably what you want
Boil it down basically? Where should matchmaking logic be held do you think?
Well it depends on how you're matchmaking I guess. By what metric? Or do you not care? Just anybody/everybody, if you're in line, you'll queue?
I guess for now it doesn’t matter that much, but I’ve got a couple data classes that hold references to ratings and division, etc so that will come into play later
Then yeah you need to throw people into a collection of some sort (maybe just a List is fine) and give everyone a weight by some metric. Maybe it's based on win-loss ratio, maybe it's based on average game score, whatever. Then when you're ready to queue, find the amount of players that you need and try to keep them as close together in weight as possible
e.g. a weight of 500 vs a weight of 550 is a lot better than a weight of 100 vs a weight of 1000. The weight difference is far too great. But you need some level of acceptable deviation
MatchMakingService perhaps?
Yeah whatever you want to call it. It just needs to have some sort of weight "matching" (for lack of a better term) system
Hmm
so a priority queue
Technically I don't think I even need this system for non tournament rounds anyway
A PriorityQueue might make things somewhat easier but you don't really have the acceptable deviation that you might want
https://paste.md-5.net/ikezamemib.cpp (context for future "metrics" used in matchmaking) it's unfinished but gets the point across I think for now
e.g. you could still have someone queue with 1000 weight but the next entry might only have 100 weight, so then what do you do?
You can't just take the next two entrants in the queue. The 100 will get decimated by the one 10x its skill lol
That's not an issue per se if it's not a tournament round
you implement WRED on top of your priority queue
I mean yeah if you don't give a shit about SBMM, just throw the next two players into a queue together lol
you may not even have to implement it, there's most likely an implementation out there
team distribution
Well at least imo, irl I'm not that good at disc golf but my goal when playing is to beat previous scores on whatever course I'm playing, otherwise if it's a torunament or something then it's actually competative as it were so
I think non tournament rounds can go without a sophisticated matchmaking system
Set up your game to use Amazon GameLift FlexMatch to build matchmaking into your multiplayer games.
Since when Amazon kek
Hmm I wonder if I should implement an average score on a course by course basis either, I think that'd be cool ie: Shawnee = avg +3.2, Parker = avg -0.5, etc etc
The scoring is based on how regular golf scoring works btw
I'm not sure what else to post for review so any aspect of the project (other than the skills kek still need to get to rewriting that), notes, concerns, etc always appreciated
If I’m a Java user, how do the examples look?
they look good
Thinking non existent
I mean not sure how Java and Kotlin interoperates with coroutines for example
@Metadata usage when
Or we rm rf Java maybe :D
^
What if we just rm rf Kotlin
nah 
Library is not meant to be interop with Java
pretty based
Shut up and write packed in java 😠
not based but alright
nah 👍
Lame
packed is open source right?
i think we need to punish rad
yup
damn that's harsh
It’s harsh but fair
Kotlin is Kancelled
damn
Suspend fun
just throw the whole packed source onto gemini and tell it to generate a java version
Codlin
Dunno about that one mate
probably works out fine
chances are it actually works and takes over packed
or we let rad do it
no
oh no
^ why not let rad go through the suffering Gemini will have to deal with in reading all that kotlin
Do feedback: ❌
Make programming language debate: ✅✅✅✅
spigotmc in a nutshell
Usually how it goes eh?
Are we debating or just circlejerking
Ladder
Tbh maybe we should have another thread to just debate controversial programming topics
Seems to be an oddly recurring theme
And you’re banned (/s)

tbf this thread doesn't get much effective use since nobody really cares enough to actually look at these codebases
unless you're making some clear beginner mistakes in which everyone will jump to tell you where you went wrong, because that's easy to point out
when it ends up being about design choices, there won't be much of a way to provide sensible feedback there
I mean its rarely the case we get coders that have more complicated questions or code that require a high level understanding of for example data relations, concurrency or some other nieche concept
.
hi
no
bye
Not downloading a random rar file from discord
Github
rawr
Please learn to use github
danger zone in repository settings
on the bottom
thanks
GitHub
Contribute to Kmyk441/DailyCommandPlugin development by creating an account on GitHub.
i guess
why did you commit your build dir
your util classes should be final and have a private constructor
im not sure whether the j.u.c. scheduler is a good idea
-
scheduler.scheduleAtFixedRate( () -> Bukkit.getScheduler().runTask(plugin, () -> Bukkit.dispatchCommand(Bukkit.getConsoleSender(), command)), initialDelay, TimeUnit.DAYS.toMillis(1), TimeUnit.MILLISECONDS );```
No absolutely not. Please use math and use the Bukkit Scheduler you know you can delay tasks with it right? as Javier pointed out this is fine I didn't think about server lag causing this to be offset and execute at an incorrect time
-
you should generally avoid hardcoding string messages
https://github.com/Kmyk441/DailyCommandPlugin/blob/main/src/main/java/me/kmyk/dailycommand/ScheduleCommandCommand.java#L21 -
don't name your main class Main.java
https://www.spigotmc.org/threads/why-you-shouldnt-name-your-main-plugin-class-main.493289/ -
public void addCommand(String command, String time) { reload(); List<Map<?, ?>> mapList = getConfig().getMapList("commands"); Map<String, String> map = new HashMap<>(); map.put("command", command); map.put("time", time); mapList.add(map); getConfig().set("commands", mapList); save(); }```
Within this method is wildly inefficent to be writing to disk every time you add one command. You should do bulk edits and only save to disk on server shutdown or over some period of time like 10 or 15 minutes.
You do the same thing with your timezone
-
public void setTimeZone(String timeZone) { reload(); getConfig().set("time-zone", timeZone); save(); }```
but user can edit the config while its running
do you mean i should do it so user isnt suposted to edit the file?
Make a reload command
You can use the java file watching API for that tbh
umm idk
the config file gets read by your plugin, then the file contents are separate from whats in the program
and using the api you can either overwrite the file, or re read it in
if they use the bukkit scheduler, it'll be offset by the server tick, and not actually executed at the time it should execute
I personally would save on the world save event
