#[MEGA THREAD] Get Your Code Reviewed or Review Others
1 messages · Page 3 of 1
Hey can I get feedback on my first plugin https://github.com/CMSombre/RiddlesFriends
404 - Private repo probably
- Missing readme file, lack of documentation
- You shouldn't post your .idea folder
- RiddlesFriendMain is a weird class name for your plugin entry point, I'd call it FriendsPlugin or RiddleFriendsPlugin
- Your plugin class isn't a data class, you shouldn't be tracking friend requests and friend data there
- You should split your command logic into a separate command class
- You shouldn't hardcode your messages
- You should use the player UUID rather than the player name in your maps and operations
- You should make your sounds configurable
- On your FriendRequestManager, don't expose a mutable list, rather return an immutable copy, or an immutable empty list (ImmutableList.copyOf, Collections.EMPTY_LIST)
- You don't use the FriendData or FriendRequestManager classes
- I'd split your IO in a separate class
Summed it up perfectly
posting .idea is fine for a subset of the files in it, such as just a few IDE settings, but most of the files shouldn't be pushed true
missed most of that (just generally skimmed trough)
For commands, you should further work on a subcommand system, or try out a command framework, but also re-use variables like args[0]
switch(args[0].toLowerCase(Locale.ROOT)) {
case "a" -> a();
case "b" -> b();
case "c" -> c();
}
reposting here from #general, you can also use switch statements
Good points for a first plugin:
- Decent variable naming
- Splitting stuff into methods
- Guard clauses, little nesting
- No static abuse
- Good usage of try-with-resources
I'll remove a point for using JSON over the industry standard YML
Adding onto this really quickly
- Don't check for things like command name
- If you are above java 16, try to use records for immutable data
- Make variables final where they can be made final
- Use packages if you need to have multiple classes of a category (like sub-command classes)
I'm old-fashioned personally and don't like records but eh
They are useful for data that won't mutate
makes it much easier to not care about boilerplate for immutable dataclasses
lombok 
no
Heyo I'm in the mood for some flames, be warned that this is just "eh whenever I can I'll add some code", no real thought put into it for now -> https://github.com/IkeVoodoo/spigot-core
that is a lot of fucking modules
ill pick a random one and look at it but im not looking at them all
looks fine to me after just some light looking but i see some system.out.println in there which earns you a 
well looks okay to me but i didn't read it like a book just skimmed around
yeah
i'll look deeper later maybe if i have time
ye sleep
I'll remove points for not making your own classes for built-in annotation processors on your config system
and rather using lambdas
these
Not a huge fan of reflections so I won't look into this further
Yep, I realized this system is completely flawed
Alright I really hate your debugging module
no
isn't this technically unsafe
where is that
nope
invalid urls yada yada
It returns the version of spigot core being used (it's shared across all modules)
The entity module?
I don't like how often you call getProvidingPlugin so I'd probably just have a variable somewhere and reuse it across the entire module
this could be switched with a more functional approach
For example, a ClickAction enum
Hm?
this never makes it clear if you're spawning the entity parts as well
Or what
I might be tripping
No no you're right
bad naming
This system could be adapted for packet entities as well, so this could be named BukkitArmorstandEntityPart
Yeah should've been ItemEntityPart
or something
Oh fair
And then a PacketArmorstandEntityPart in the future
So you could then do a BukkitDisplayEntityPart
etc etc
you get the idea
Yep I agree
ItemSelector is a bit nifty
how so
Not a fan of how you're hardcoding next and previous to be final
For example, on some menus I've made, if you were in the first page you don't have a previous button and it is another item
nvm you can't set them
got the wrong class lol
oh no you def can, but yeah it should be like an item stack provider
and not a final item
I'd rather make a MenuElement interface with impls such as Button (itemstack / supplier), Switch (multiple items, each has its element) etc
and nuke the itemselector class in favor of switch
Oh yeah
SelectionConverter could be useful for the multiswitch but eh
I'd only give menu context to my elements on the getItem function, maybe..
ItemSelector shouldn't be responsible for managing its own screen
unless your Screen is my MenuLayer
even then, icky
👍
ScreenPaginator in every screen? fuck no
what
Just make a layer system with a SimpleLayer and a PaginableLayer
Code review? https://github.com/Sweattypalms/skyblock-remake
apart from the name, there is nothing in here to make you believe its skyblock
could be for any rpg like plugin
I think thats what illusion means
im not really a fan of the static lists you have for mobs and items, etc
I would just use DI if you can
you wont be needing those lists in a ton of places I reckon
Don’t use sysout to log stuff
Lua spigot coding??
Yup
I've never seen something so cursed that I also loved so much
god dammit is that lua?
ofc
I was literally looking up your name to see when your last message was because I thought I haven't seen you in a while and you appeared lmao
Can someone check my main class again, I did more methods and switches this time around
return friendData.containsKey(player) && friendData.get(player).contains(friend); ee two lookups
Translation:
FriendData friendData = friendData.get(player.getUniqueId()); // Use UUID instead of Player, it's safer
// "friendData != null" is the same as containsKey
return friendData != null && friendData.contains(friend.getUniqueId()); // Again use UUID instead of Player
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L23-L25 these fields could probably be final (the initialization would then happen in the constructor or init block)
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L51 This could also be for Command blocks, FYI
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L47 and https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L74 errors or warnings are usually colored red (same with pretty much every other line)
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L82-L84 mkdir already has that check embedded, iirc
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L139-L140 may be vulnerable to attacks due to the complexity of #contains in a List being O(n) and the fact that you allow invalid entries and never purge entries (even across restarts!).
They do not use Player - don't worry
ah alright
However it is still better to use UUIDs over Strings.
friendDate.getOrDefault(x, Collections.emptyList()).contains(friend) 😉
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L128 returning an empty list (via Collections.emptyList()) may be better here
just check for null, youre not reusing it
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L130 long friend lists aren't broken up in multiple messages and the format of ArrayList#toString isn't actually that user-friendly to be worth using.
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L148 this check has already been performed on line 139, you might want to not duplicate it.
Furthermore you may want to replace all mentions of /friend with the respective alias that was used to invoke the command, just in case the server owner defined an alias via the commands.yml file or even overwrote the command (which can be necessary in case of plugin conflicts)
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L101 this is a very ... strange line. Are you sure that makes sense?
Additionally, to some degree I'd recommend using NIO instead of java.io.File but as bukkit is already using java.io.File there is no notable difference
That isn't actually viable as you can remove friends but in that case the list of friends may be empty.
Well you could still have a null check, but the isEmpty check is still required
https://github.com/CMSombre/RiddlesFriends/blob/master/src/main/java/me/cmriddles/riddlesfriends/RiddlesFriendMain.java#L242 you can always return true here. Instead, isFriendRequestPending is replaced by removeFriendRequest (at line 240)
Hey guys, I updated my plugin, can I get feedback
https://github.com/CMSombre/RiddlesFriends
3 classes isnt a lot
huh?
To what extent do you want feedback?
at my code, cuz I'm new to programming plugins
Is there anything in particular you’d like me to look at?
just the overall structure if theres anything I can improve on, maybe my DatabaseHandler and FriendDataCache, I have 34 warnings and idk why
Perhaps start by going through then lol
Your DataBaseHandler isn’t a handler
Its called Database not DataBase btw
You can turn it into a record btw
Your FriendDataCache isn’t a cache
You just directly talk to mysql everytime
huh? isnt that the point of the cache, to relay to the database
and the handler connects to the database while the cache relays info the database?
Nah, I mean when we talk about cached data, we mean data that’s stored somewhere where its fast and easy to access
Database calls take time, a lot of time and are heavily io intensive
So if it were a cache, it would be that you store the data accessed from mysql in some sort of Map or List, or other data structure during the runtime of the server
In that way its accessible and fast to access
ahh, so my class names are irrelevant then to what I coded
hi concLUBE how are you
And DatabaseCredentials or sth
Im finw hbu
im good just chillin
nicee
Its decent
I mean obviously you should probably learn the object oriented paradigm in java if you now code in java
That means Object Orientation, read Effective Java, learn SOLID, learn a bit about Functional Programming in Object Oriented Fashion, learn a bit about the Data Oriented Programming paradigm, and learn how to deal with concurrency in Java
Now that’s a lot of stuff
So don’t go on and learn everything I just mentioned
But pick something, and try to implement it in your code (:
thank you for the feedback
the class extending javaplugin should be NamePlugin
Meh not necessarily
Thats kinda cringe!!
No that’s actually a good point @lunar vale has
Since its a derivative of JavaPlugin, where the common term is Plugin (it even appears in the superclasses of JavaPlugin), similarly people do this with different types of exceptions, Exception -> RuntimeException -> IllegalArgumentException etc
I'll also add that it should be the only class in that specific package
or have its own package
Good rule of thumb also
For example
- Don't name your listeners package "events", that implies that's where the custom events are
- Don't bury important classes deep into packages, they should be intuitive to find if I'm just skimming through the folders
There is also a good name convention that packages are to be named singular
Just look at the std lib
My main classes are usually called Name
Except for Presence where it is called PresenceBukkit, even though it doesn't run on bukkit.
if you had a package to store settings related objects, do you call it setting or settings?
That’s extremely abstract
But for instance if you have a package of utilities you call the package util, and for commands you call it command etc
I know it has been 2 days ago, but...
c# for spigot when?
SpigotMC#
SpigotMC++
SpigotMC
A
S
S
E
SpigotMC
B
L
Y
Spigot is a multi-language minecraft server 😎
Btw, I've made some trashy command execution for a json database engine I've made
https://github.com/KarmaDeb/KarmaAPI2/blob/master/KarmaAPI-Core/src/main/java/es/karmadev/api/database/model/json/JsonConnectionExecutor.java
Any suggestion about improving it?
(other than simplifying and splitting into methods, which I'm currently working on)
ow what the fuck
Yes
there's a lot of room for improvement
thinking of a Map<String, Predicate<String>> comparators
and some other stuff
not a fan
Wait, how could I use predicate instead of patterns for it?
No way, I just realised
You are right
Anyway, there're some things I still need from Pattern, such as grouping
Cache them
And use named groups
Because you have so many
Oh, completely forgot naming groups was a featur
Anyway, any other recomendation or bad practice that I'm doing?
There is a convention that a regex should be as strict as possible
So that ofc means avoid wildcard matching if possible
mainly because regex that matches against a lot is easier to break, since there are plausibly more edge cases around that pattern
(Tho this is mostly important if the regex is used to match end user input)
SneakyIllegalRuntimeIOException
perfect
UncheckedIOException exists
https://github.com/KarmaDeb/KarmaAPI2/tree/master/KarmaAPI-Spigot/src/main/java/es/karmadev/api/spigot/command
Simple command framework for spigot, it allows you to create sub-commands in a easy and "native" way
https://github.com/skinnynoonie/CharacterWar
I'm aware of the lack of docs in the readme
but I would like some help with organizing this and would like some opinions
public static CharacterWar getInstance() {
return JavaPlugin.getPlugin(CharacterWar.class);
}
Don't ever get instance like this
If you're going to use q singleton do it properly
private static CharactarWars instance
onEnable()
instance = this
that's how I used to do it
but idk I think I've seen a forum saying u could do that too
That's how it should be done if you're going to use the main clas sas a singleton
but what's bad about it
Whoever said that on the forums is an idiot
I'd have to pull up source but iirc it uses reflection
Forget how that's stored exactly but it'll always be better to do it the way I provided
doesn't
What if it's just a map of class and instance
Still would be better to use the way provided above
it gets the classloader of the provided class, checks if it is a plugin class loader and if it is it gets the stored plugin instance
ok
public boolean itemIsSimilar(ItemStack itemOne, ItemStack itemTwo) { foe this method in AbstractCharacter don't replace the item with air if it's null if you know either one Is air just return false. The method doesn't seem like it's in the right place to begin with though it should probably be in a utility class
ok
You also really shouldn't be comparing items with display name you should be using PDC
I'm not gonna remove the null part
?pdc
cause bukkit api is inconsistent with AIR and null
That is true but you can remove the assignment to air and still ensure the end method contract is the same
public void registerCharacter(Character character) { try { in this method don't try catch class cast exception. You can just do an instance of check. Or better yet if every character will have a listener have interfaceCharacter extend Listener instead of having AbstractCharacter implement it
it doesn't make sense for Character to extend it tho
It does based on how that method works
You're throwing if it's not a listener
If it's not unexpected do instanceof and don't throw an exception
I thought about splitting the Character where Character is like the information class, and then it also had a CharacterItemSet where it implements all the items
@woeful pasture
what do you recommend doing on getting the pdc
cause each every will trigger and try to filter the pdc
so there's going to be like 50 characters all loading the pdc and only 1 of them does the actual functioning
Getting the PDC is not much less efficient than getting the name
The change in performance is negligible
no I mean like
lets say I have 50 characters
and each one of them is a registered listener that all get the pdc in the event
that's getting the pdc 50 times
It's no worse then getting the display name 50 times
Though slightly higher memory overhead which is negligible on modern systems
bro
im saying
im not comparing the two
im saying will my server lag if I do that
You also shouldn't need to grab the PDC for every character it should be done in O(1) operation you need to know which character a given player has equipped
That way you're not filtering 50 characters instead your simply doing it once for the character you know it is
no?
people can mix and match the character item pieces
the way you represent your data is then misleading and not ideal. You'd really want a some data structure of Custom Items rather than doing something like a Character interface.
You can represent this data by making some form of registry.
Map<KeyValue, ItemData>
To further expound. You really should be handling these items like Custom Items rather than a complete set if they can be mixed and matched. Each ItemData can have a handle for an event. Note you really shouldn't register these as event handlers rather call them when they're needed within a core listener for example
public onEquip(PlayerAmrmorEquipEvent event) {
key = event.getPlayer.getHelmet().geOurSpecialKey();
OurItemCache.getItem(key).callEvent(event);
}```
in this case you could likely get away with only using a String for the KeyValue and just putting the given string in PDC
then you have a constant key which holds the item ID
e.g. item-id = speedy_boots or item-id = wings
check updated message above
just note that the above code is sudo code so it won't work
class CustomItemEventAdapter {
public void onDamageEvent(EntityDamageEvent event) {
}
}
and should I just have all the events
wait what
u can use .callEvent
wtf
never knew that
oh
Its sudo code it won't work
should I do that
I think Java Discord API does that
yeah pretty much sometihng along those lines. Just remember you should be doing something like this for your custom item class though
public abstract class CustomItem {
... // blah blah other data here that is consistent against all other items
public void trigger(PlayerEvent event) // you can use generics here or just case {
// DO STUFF WOOOO
}
}```
it should return a copy of whatever your base item would be
ItemStacks are mutable so it has to be a copy
@woeful pasture
bruh
do I need to dependency inject my namespaced key
to all the items
wtf no, what are you even doing you can just create Namespaced keys on the fly
tbh I'd save them as static variables
k
https://github.com/skinnynoonie/CharacterWar
Did some re modeling
I use PDCs now, and each item has their own class for their own impl
Things I will be doing so dw abt them:
- Documentation
- Command API
- Maybe configurable messages
It is a remake of hypixel skyblock. Not normal skyblock
What's that?
?di
Guide to dependency injection: https://www.spigotmc.org/wiki/using-dependency-injection/
oof its case sensitive
oh I see
I don't really save mobs in a list
I save their constructors
and same for items
The mobs's NMS implementation implement a custom interface to get the SkyblockMob class
I don't use mappings of MOBS to UUIDS or any bs
can someone review
please
- Don't expose mutable collections (CharacterManager#getCharacters should expose an immutable copy)
- Null validation for getCharacterByReferenceName
- I'd make a dummy CustomItemImpl rather than returning null.
- I'd make a ConfigurableCharacter class and load characters from the config, there's no need for them to be hardcoded
- Messages could be configurable
- You have a MessageConstants class yet keep your message constants in your command class
- ItemListenerAdapter could be replaced with a fancy generified
subscribe(eventClass, eventConsumer)method - I have no idea what's going on the CustomEntityDamageEvent, the class name suggest it's an event class but it's an interface/data class combo
- Your Commands class doesn't check if the command is registered in the plugin.yml, error prone
Bunch more stuff, I'd structure the project in an entirely different way so I don't want to start nitpicking
- the name should be instance, not INSTANCE
- use
thiswhenever possible, it adds readability - weird usage of packages,
me.skinnynoonie.characterwar.character.implsandme.skinnynoonie.characterwar.characterhave no relationship, don't treat them as child-parent - your character manager class is just a repository class, name it more precisely
- the term "reference name" is weird, find a better way to convey the meaning behind whatever that is
- your cooldown manager class has a static method over the constructor, switch order
- use variables when you know the instance is gonna be called multiple times
if (cooldowns.get(uuid) == null) {
return true;
}
if (cooldowns.get(uuid).get(key) == null) {
return false;
}
return cooldowns.get(uuid).get(key).expired();
``` very little reason to do this.
- name packages singularly
- ur commands class is not a util class
- you use singletons and dependency injection oddly, go with dependency injection if u feel unsure what to use
- your item listener adapter isnt an adapter class
- your custim item impl class isnt an implementational class
- CMD is a weird acronym suffix for a class, consider writing it out
that class is not a primary example lmao
just because they do it doesnt mean its inherently used correctly
anyway, @night knoll i dont know if they use the pattern correctly (but you were not doing it at least)
yeah I just read that code
it looks like it does something more than just being an interface with a set of abstract methods (the reflection at the very bottom of the source file)
bruh
its really a trap, even relatively well known projects at times may have poor naming choices
Yo I'll be back tmr guys
yea gl :)
@round fossil
What do I call the class then
cause I like it how the method name correlates with the impl
just name it like ItemListener, ItemObserver, ItemHandler or sth idk
just as long as the name is not misleading or irrelevant
But it deals with abstraction for events
yes I see that
ItemListenerMethods.class
but its not adapter just because of "But it deals with abstraction for events"
no no no
just ItemListener is fine
CustomItemListener
an example from mojang (a bit more... well... correct one) is the nms entity Villager
it implements
public interface ReputationEventHandler {
public void onReputationEventFrom(ReputationEventType var1, Entity var2);
}
which is some type of even listener abstraction
Is the Mc src good
not always
they have bombardered their code with design patterns, basically overengineered it
isn't there professional devs and stuff
yes, but that doesn't mean you need to overengineer everything
iirc at one point they had a singleton for sneaky throwing
@round fossil
yea
So
like skinnynoonie, let me just get this straight with u
ImIllusion said my custom event impls are misleading
there is no src thats good to the very end
what and how should I call and organize them
All they do is send events to the ItemListeners, so I don't spam get the PDC
yes its misleading
because in bukkit you'd basically think its one of those PlayerJoinEvent, PlayerQuitEvent etc
PlayerInteractItemListenerImpl
uh sure
one good starting point is to get rid of poorly named classes, variables and packages
second is to not take code for granted
just because its written by a reputable developer doesnt mean its impeccable
try to be critical, what would I have done here? what do I not like here? what do I like here? why is this good/bad?
how should I organize my characters
cause character. Is for all the managers and other stuff
And impls. I also thought was kinda bad
ur character class sucks
But I also thought characters. Would be too long
u use abstract classes wrongly
well it is abstract
and?
the abstract there doesn't achieve something meaningful in comparison to other semantic features
like
you probably just want an interface
and an impl
w/o the abstract
i am gonna get hate for this one @night knoll , but I generally believe abstract classes are one of the easiest tools to make ur code smell in java
they allow extremely tightly coupling in the shape of parent-child dependence
if possible, use interfaces with default methods and then just concrete implementations
nah I did that
public abstract class Character {
private final CustomItemImpl helmet;
private final CustomItemImpl chestplate;
private final CustomItemImpl leggings;
private final CustomItemImpl boots;
private final List<CustomItemImpl> items;
public Character() {
helmet = getHelmetImpl();
chestplate = getChestplateImpl();
leggings = getLeggingImpl();
boots = getBootsImpl();
items = getItemImpls();
}
public abstract @NotNull String getReferenceName();
public abstract @NotNull String getDisplayName();
public abstract @NotNull String getDescription();
public abstract @NotNull CharacterVerse getVerse();
public abstract @Nullable CustomItemImpl getHelmetImpl();
public abstract @Nullable CustomItemImpl getChestplateImpl();
public abstract @Nullable CustomItemImpl getLeggingImpl();
public abstract @Nullable CustomItemImpl getBootsImpl();
public abstract @NotNull List<@NotNull CustomItemImpl> getItemImpls();
this is not an interface
and the reason I didn't want to use an interface because I didn't think it was worthy to make many instances of an item impl
look at the pre-made methods
but there is more commitment to an abstract class than to an interface tho?
you're coping rn
I don't think it makes sense to basically do new SupermanLeggingsImpl().getItem(); every time
someone gets a kit
I mean you would cache SupermanLeggingsImpl surely?
how
throw away objects is generally considered an anti pattern
i mean just create a repository class
What's that
a class whose sole purpose is to allow access to data(-transfers) somehow (often through key-value)
for instance
class UserRepository {
Map<String,User> backingMap; //with getters and setters without exposing map
}
class CharacterTypeRepository {
Map<String,CharacterType> backingMap; // -||-
}
class Character {
CharacterType type;
User owner;
}
class User {
Map<String,Character> userCharacters;
Character currentCharacter;
}
then sth like
UserRepository userRepo = ...;
CharacterTypeRepository characterTypeRepo = ...;
Storage storage = ...;
User user = userRepo.findUser(uuid);
storage.loadData(user).await();
Character character = user.createNewCharacter("name", characterTypeRepo.getByName("superman"));
user.selectCharacter(character);
storage.saveData(user).await();
sorry but I ain't doing all that
interface CharacterType {
//an optional accessor
@Nullable public default String description() {
return null;
}
//a required accessor
public String name();
}```
u dont have to do all that
but u get the idea
you asked for a code interview, I will point out code smells based on where ur code is most likely gonna be an issue in the future or for ppl who read in the future
if u wna apply it or not is up to u
ik how
as said the issue with abstract classes is that they cause TIGHT COUPLINESS
Character interface
AbstractCharacter abstract class
BatmanCharacter impl
and when they dont cause tight coupliness they basically just do what interfaces do already
boom
lol why the AbstractCharacter
Cause u like ur interface so much
ur character is just a fkn data transfer anyway
?
no im asking u, its not about me
yes so why would u have an abstract class for it
there is nothing to make abstract in the first place
not if I make the interface
u dont get the point
ill re-iterate myself
abstract classes are one of the easiest tools to make ur code smell in java
they allow extremely tightly coupling in the shape of parent-child dependence
and when they dont cause tight coupliness they basically just do what interfaces do already
What does tight coupling mean
it means code is tightly dependent on the components it depends on
What does it depend on
for instance if ur code is binded to an instance (like a singleton) then its tightly coupled
it's not dependent on the abstract class
Bukkit.getServer().getOnlinePlayers() is a tightly coupled method call, because whatever class that calls this method depends on an instance
no but the issue with abstract classes is that all subclasses of that abstract class are gonna be extremely dependent on the concretion of the abstract class to work accordingly
No
How
quite literally
no but the issue with abstract classes is that all subclasses of that abstract class are gonna be extremely dependent on the concretion of the abstract class to work accordingly
Send me the superman character class
urs?
Yes I'm on phone
Like in dc
no thats spammy
Alr hold on
I'm brushing my teeth
as said again, THE REASON YOU DONT EXPERIENCE THE ISSUES WITH ABSTRACT CLASSES IS:
- your code is like 500 lines in total, u dont have any issues changing stuff because the project is so small
- you dont even use the abstract class in a way that it utilizes the way abstract classes are supposed to be used, your implementation sucks, it can be implemented with just interfaces and normal classes
also because you don't really understand the difference between type dependence and unit dependence
prob no issues unless u wanna scale ur project
but u went here for a code review
so here u get one
the way u used the abstract class smells, no matter how u try to advert ur eyes, im not trying to insult you, but im just emphasizing there are better ways to do what you did there
if you dont care about improving the quality of code there is really no point in asking for a review
How tho
with an interface
bro
quite literally
dont bro me, you know what I mean and what I have been implying all this time
its really not
I don't rly see how ima run into any issues
the Character abstract class is just a data holder class
There's no impl
nothing
it's like an artifact at a museum
you dont even know what impl means
don't start insulting me
im not
the only tight coupling that's gonna happen is between me and u
you drop this, dont ask for code reviews again if you're going to behave like this
Explain to me
Why doing it your way is better
like I still don't get why I can't just use an abstract class for this
its not like you cant
i say it again, there are better ways to do it
one is with just an interface
idk why u have user class and why a character class has a owner
And then a String, User map
... ok
ok so let's say I use an interface
What am i gonna do about those throw away objects
Abstract classes promote what's called implementation inheritance which is when sub classes strictly rely on their parent class structures, the means when the parent class implementation is changed, the subclass has to change accordingly. If this does not apply to your abstract class then you use your abstract class wrongly, which means it can simply be done with an interface and some concrete classes. If this does apply to your abstract class then you have a proper use of the abstract class, but it comes with the cost of this tight sub-super class relationship.
whether u want to have throw away objects or not is independent of if u use an abstract class or an interface
ok so
Character - interface
AbstractCharacter - abstract class
SupermanCharacter - impl
Character changes, abstractcharcter will too
yes but if Character changes, the the contract of ur component's functionality is changed
so of course the underlying implementation has to adapt to the change
im talking about implementation changes
the fragile parts of code
AbstractMap, AbstractList etc exists because they are skeletal implementations
(no one ever depends on these types, they are just there for convenience without damaging maintainability, structure etc altho it does affect flexibility)
yours is not
No?
yes it does
ok so I use an abstract class
cause it has no functionality
Other than displaying sata
data
bro chill out
this is not getting anywhere
Then what do I do about the throw away objects
you refuse to listen anyway
u never answered that question
how would that look like
look at my example for instance lmao
u have a map with keys, and then normal plain java classes
CharacterRegistry - sort manager class?
Character - interface
SupermanCharacter - impl
yes
like
thats good
Like what
CharacterRegistry {
IdentityHashMap<Type,Character> mapByType;
HashMap<String,Character> mapByName;
HashMap<Integer,Character> mapById;
int id = 0;
void register(Character character) {
mapByType.put(character.getClass(),character);
mapByName.put(character.name(),character);
mapById.put(id++,character);
}
//get by id
Character byId(int id) {
return mapById.get(id);
}
//get by name
Character byName(String name) {
return mapByName.get(name);
}
//get by type
<C extends Character> byType(Class<C> type) {
return (C) mapByType.get(type);
}
}
example only
ok bro what
im just trying to give u an idea or well inspiration
yea
so when u need the leggings u'd just:
registry.byName("superman").getLeggings();
Not possible
why?
mhm
then it would be just
registry.byName("superman").getLeggingsImpl();
no?
i mean u get the point i hope
reuse the objects
getItem is not a throw away object
because it creates an itemstack which u would most likely do something with ofc
itemstacks are also fine to create - use once - and repeat
@round fossil
yes but u dont need abstract for that lol
yes because you base yourself around the importance of that the abstract class is useful because its abstract or something
It is abstract tho
ru trolling?
yes because you base yourself around the importance of that the abstract class is useful because its abstract or something
Oh
bro
ok so
let's say I use the design
U gave me
How is .getItemImpl not a throw away obj
i mean ofc u have to tweak it
I cant
let me give an example
Unless I dependency inject the itemstack into the item impl
interface Character {
@NotNull String displayName();
@Nullable public default CustomItemImpl leggings() { return null; }
}
class SupermanCharacter implements Character {
private final String displayName;
private final CustomItemImpl leggings;
public SupermanCharacter(String displayName) {
this.displayName = displayName;
this.leggings = new SupermanLeggings();
}
@Override @NotNull public String displayName() { return this.displayName; }
@Override @NotNull public CustomItemImpl leggings() { return this.leggings; }
}
(last line):
@Nullable @NotNull public CustomItemImpl leggings() { return this.leggings; }
if the impl does not have leggings, u just dont override the method
ok that's just stupid
what is?
instead of letting another class be the cache, the display class has to cache itself
u cache it in another class if u want lol
So now I gotta force myself to write a bunch of boiler plate
no one is restricting u
not rly
u can prob recordify it
if u dont like the boilerplate, either go with a record, or use lombok or sth like kotlin
abstract is not the way
you can prob ask any1 here, they'd agree with me (i hope at least)
so u want it to be abstract?
yes
thats another goal, but well, idk, it doesnt really suit to be abstract there let me see
if u couple the event handling then maybe it would make sense
like let stuff such as
onPlayerDamagedWithArmor(@NotNull EntityDamageEvent event, @NotNull Player victim) be abstract methods in ur abstract class
and impl it in SupermanCharacter
i still dont like it
but having that there would justify it more
yeah so instead u resorted to an interface CustomItemImpl and let each item impl it
which is good design
i like it
Thx
the name Impl in the end is questionable since its an abstraction but w/e
u got the concept right
thats whats important
bruh rly changed pfps mid convo
me?
yes
ok well
I didn't want a compliment
the world wont end
but like as mentioned, it doesnt need to be abstract and shouldnt be
ok then what should it be
normal java class
I remember someone told me
dependency platform development
Where it's interfaces for everything
And then abstract for boiler plate
Then the impls
who said that
i will quite literally bonk them in their feet
its partially right
but its not the whole truth
anyway i need to sleep now
gn, can continue tmrw
@jagged rock I think
yes but illusion doesnt write a lot of unit tests, and havent really been in that industry, so he uses abstract classes a lot since he hasn't rly experienced the pain they may bring
(this structure mostly works for platform-agnostic code)
ok so
if u code in mc u prob wont experience issues with abstract classes
ok so it's fine
what I gave is an industry standard review
(where the difference between each module is about ~10 lines of code)
i mean its fine to have everything static if u're mentally unstable
bro
(I rarely use abstract classes when working on spigot-only projects)
not not really
there is a huge difference
fields being one of them
second being that interfaces are way easier to mock
so when it comes to transparent testing abstract classes become a huge problem
Well in my case I make an interface and expose that interface
The abstract impl just de-duplicates code :)
If you want to mock stuff, you mock the interface
yeah its just a skeletal implementation ^
like AbstractMap, or w/e other abstract thingy u wanna bring up as an example
ok so lmk why this won't work (ik I've mentioned this so many times)
Character - interface
AbstractCharacter - abstract class
SupermanCharacter - impl
Has an interface so it encapsulates a lot of stuff
Abstract is abstract, has some boiler plate reduction
Impl - the impl
since when was abstraction not allowed for data
There are use-cases
@jagged rock how would u do this
I'd honestly make the entire Character part just interfaces
And config files that point to abstract items
but tbh you barely need an abstract class for custom item logic
^
yes but boilerplate is invalid reason
if u wanna avoid repeating yourself
I use abstraction for DRY reasons
then yes use abstract to skeletally abstract away repetitions
yes but boilerplate is something else
java has it by nature
if u wanna avoid it, use lombok or kotlin ig
(ignore the broken imports)
This is how I do cosmetics
but like
if I were to make it now this would be an interface
yeah thats more reasonable
yeah but u have the
anyway
avoiding boilerplate means avoiding writing out stuff like the {}, semicolons, basically unnecessary code thats just in the way and can be reduced but still produce the same semantics
avodiing repetition means you avoid writing out the same variable, the same method again etc
abstract class if used as a skeletal class, then it should be due to the latter
if u do it due to the former, then there are better tools to think about first like records, lombok, other languages or something else
static
there is also the argument of that you should only ever abstract away if its a useful abstraction, is an abstract class for a data transfer object useful? probably not, at least 99% of the times
For example this last class could've been reworked to proxy a Renderer interface and avoid the abstraction
bro I'm so confused
yeah, sounds way more clean
what u feel is best
Bros about to cry in a corner out of confusion
when u understand object orientation, data orientation and design good enough you'll get it intuitively
That's what's best
:,)
bruh
no
@round fossil @jagged rock r u guys telling me I should create the item stacks in the character impl class
No
We're saying to ditch the abstract class unless there's a real good reason to keep it
generally speaking, the more responsibility given to the character class, the harder it becomes to maintain it
So all the responsibility goes to the impl?
if u want
it makes the impl more complex and harder to maintain
there is pros and cons to everything
Ever heard of Single Responsibility Principle?
It's a bit easy to get it wrong but basically you should write your class to serve a niche purpose
And nothing else
The more complex your project is, the more niche each class is
For example, people can say "The admin command class handles everything regarding the admin command"
BUT
The admin command should only handle the command logic itself
and the abstract character handles the cache
Any data that's manipulated by the command needs to be handled elsewhere
So I'm following srp
in short, srp says a class should only change its implementation for one major reason, the Database class shouldnt have to be changed because your LoginHandler needed to change for instance
It's not
its up to the developer to define these so called major reasons
the abstract one
Then what should
its fine if ur character class refers to WHAT items are relevant, but it shouldnt contain HOW those work or their implementations
I'm thinking
public interface CharacterInventory {
ItemStack getItem(EquipmentSlot slot);
Collection<ItemStack> getExtraItems();
}
public interface CharacterInfo {
String getName();
String getDescription();
CharacterVerse getVerse();
}
public interface Character {
CharacterInventory getInventory();
CharacterInfo getInfo();
}
thats really good data structuring as well
skinny, this is prob the type of structure u're looking for
That looks a lot more promising
where do I put the impl
Eh actually idk
that seems like a lot of classes for one character
Now, you could do something like
public class BasicCharacterInfo implements CharacterInfo {
private final String name;
private final String description;
private final CharacterVerse verse;
public BasicCharacterInfo(String name, String description, CharacterVerse verse) {
this.name = name;
this.description = description;
this.verse = verse;
}
public BasicCharacterInfo(ConfigurationSection section) {
this.name = section.getString("name", "FIX YOUR CONFIGURATION - INVALID NAME");
this.description = section.getString("description", "FIX YOUR CONFIGURATION - INVALID DESCRIPTION");
this.verse = CharacterVerse.valueOf(section.getString("verse", "DC").toUpperCase(Locale.ROOT));
}
@Override
public String getName() {
return this.name;
}
@Override
public String getDescription() {
return this.description;
}
@Override
public CharacterVerse getVerse() {
return this.verse;
}
}
public class BasicCharacterInventory implements CharacterInventory {
private final Map<EquipmentSlot, ItemStack> items = new HashMap<>(); // Use whatever impl you want
private final Collection<ItemStack> extra = new ArrayList<>(36);
public void registerEquipment(EquipmentSlot slot, ItemStack item) {
if(slot == null || item == null) {
return;
}
items.put(slot, item);
}
public void addExtraItems(ItemStack first, ItemStack... other) {
if(first != null) {
extra.add(first);
}
extra.addAll(List.of(other));
}
@Override
public ItemStack getItem(EquipmentSlot slot) {
ItemStack result = this.items.get(slot);
return result == null ? null : result.clone(); // ternary, replace with if-else if you want
}
@Override
public Collection<ItemStack> getExtraItems() {
return List.copyOf(this.extra);
}
}```
bim bim bam bam
But tbh I'd rather skip this entire inventory BS
Have a registry somewhere
Your pdc string links to your custom item impl, not to the character
I'd probably rename CharacterInventory to just ArmorSet
and load and register them separate
linking them to a custom item registry
Up to you
You could use an abstract class for this, depending on how you plan for it to work
yay abstract class
My usual item impls look like
public class WhateverItem extends CustomItem {
public WhateverItem(MyPlugin plugin) {
super(plugin);
subscribe(PlayerInteractEvent.class, this::handleClick);
}
private void handleClick(PlayerInteractEvent event) {
if(!event.getAction().isRightClick()) {
return;
}
ItemStack item = event.getItem();
if(!matches(item)) {
return;
}
...
}
@Getter
public String getName() {
return "whatever";
}
}
but it can be replaced with an interface if you adventure enough
public interface CustomItem extends Listener {
default boolean matches(ItemStack item) {
String name = getName();
// get pdc string
return ...
}
String getName();
}
replace the subscribe with an @EventHandler
And register the listener when you register the item in your registry
ok but the PDC string won't be in the item
it will
where
somewhere
here's what I do with uhh
abstract classes
old impl
wrote it a few months ago
@jagged rock what do you use to config items
oh I meant like
do u use an API
I make all my utilities
something like that
lmao
@jagged rock yo idk how this gonna work ima be honest
My abstract class way makes the most sense to me still
maybe I'll rename it to CharacterTemplate or something
nah nvm
Interfaces are pretty abstract and can be hard to think through especially if your new
I'd reccomend sticking with it though maybe do a formal write up for your plan
@woeful pasture
I talked to someone and they said abstract classes managing data is bad
and it should manage functionality
U agree with that
ofcourse
abstract classes exist to manage basic implementations not enforce and manage data
ofc this depends on how you define "manage data" abstract classes having some basic set of data they work with isn't inherently bad
I'd say its maybe a bit more nuanced than abstract classes managing any set of data is bad. Remember though I'm pretty new to programming (only 2 years with java) so there is always a chance my statement isn't fully accurate
skinny, have u ever looked at AbstractList, or AbstractMap
they are abstract classes, mostly for convenience if u wna implement a custom map or list
I looked at AbstractList @round fossil
and that's what they told me to look at and it's what I noticed
yeah, so it exposes like one abstract method to minimize the complexity
if u try to extend AbstractList
u'll see there is only one method u have to override
Because I don't know how I can check if someone's boots is superman's boots in my item implementation
and that also means the character class will define the PDC itself
Which idk if that should be done in that class
what else should define the pdc
Idk it's just that it seems kind of off
let's say I have something like
public ItemStack getBoots() {
return new ItemBuilder(Material.LEATHER_BOOTS)
.setPDCValue(key, "reference-name").build();
}```
Idk how to explain
Factory?
interface ArmorFactory {
ItemStack head();
ItemStack chest()
ItemStack pants();
ItemStack boots();
}
public class DiamondArmorFactory implements ArmorFactory {
ItemStack head() { /* Returns new diamond helmet itemstack */ }
ItemStack chest() { /* Returns new diamond chestplate itemstack */ }
ItemStack pants() { /* Returns new diamond pants itemstack */ }
ItemStack boots() { /* Returns new diamond boots itemstack */ }
}
factory
Factories are insanely hot both literally and figuratively
If you have a factory in your code, you are one step closer to marrying me
No sorry.
I don't think this was a question 👀
Aight what's step 2
Send me 17000$
public interface EquipmentFactory {
ItemStack create(EquipmentSlot slot);
}
🤤
public class ConfigurableEquipmentFactory implements EquipmentFactory {
private final ConfigurationSection node;
public ConfigurableEquipmentFactory(ConfigurationSection node) {
this.node = node;
}
@Override
public ItemStack create(EquipmentSlot slot) {
String path = slot.name().toLowerCase(Locale.ROOT);
ConfigurationSection itemSection = node.getConfigurationSection(path);
if(itemSection == null) {
return null;
}
return ItemBuilder.fromSection(itemSection); // replace with your own util
}
}
is how I'd do it
💍
the only problem I have rn is the namespaced key
i want an easy system to manage it while being abstract with it
because every item will use the same namespaced key like this
{"reference_name":"name"}```
@night knoll is your code on github?
yeah but it isn't updated there
push your changes and send me a link
i havent made any updates
im still tryna figure out how to do this
you can just make your key on the fly using the static methods from namespaced key
bruh
I do it without passing my plugin instance. altho thats generally considered a bad approeach, but that way I can create true constants so to speak
I do to the plugin instance is just inconvient
you can always do static final fields with the plguin instance if you use static getter
yeah, its just that its a bit ugly and depending on how u design it, it may prove inflexible
hence why I avoid it, altho for 99% of users, it wont matter :>
@round fossil
epic conclube mention
is it OK if I make an abstract class to help a functionality
what functionallity
like
public item getItem() {
return getItemBase().setPDCValue(a, a, a);
}
public abstract item getItemBase();
What does that mean
i would say if setting the pdc value generates the final item id call it someting like build() so you can do something like Armor#build()
The main issue I have is setting, then getting the correct PDC key in something like a damage event
and I don't wanna directly hardcode those values in
then make a class that fills them in for you
is it ok to nest all that under item
or should I put them in a new package called "custom"
I think this is fine
ok thx
would be redundant
only extra package i would see there is for armor
https://github.com/skinnynoonie/CharacterWar review please, I tried to remake it according to some advice I received
looks gtm on the first look. only thing i noticed are the single implementation interfaces which are redundant
but abstractions for no reason just makes things more complicated
You're talking about the repositories right
Yeah there’s no such thing as a zero cost abstraction or whatever it goes like
idk but if I were to do this how i would like it
I would delete the CharacterInfo interface
And the CustomCharacter interface
And obv delete the repository interfaces
Rust moment
Not 0 cost and this is not the channel, I’ve already brought up this once with you
hm?
also what do you mean by "I've already bought up this once with you"?
You went off topic in this channel before
thats good
well I got yelled at for having them
Everyone thinks of stuff differently
by whom?
probably this
https://github.com/ToastArgumentative/ToastRPG
Just wanna know how it's lookin so far.
It seems like you're trying to go into enterprise design before mastering the basics
The first two things I notice when I open your project are:
- Static abuse
- Not following naming conventions
wdym Static abuse?
naming conventions i get, i just can get into the habbit. they arent consistent at all.
I just made changes to the static methods.
Still not good
just dont over create