#[MEGA THREAD] Get Your Code Reviewed or Review Others

1 messages · Page 11 of 1

dense leaf
#

Well it's actually 384 sprints because-
fuck off annoying PMs 🙃

junior holly
#

You guys would love my father

#

He's been a project impl manager for like 28 years now

#

Basically an architect minus being a dev

dense leaf
#

My father works with VB at work

junior holly
#

What is vb

dense leaf
#

Virtual Basic

late smelt
#

Visual Basic

dense leaf
#

same thing

junior holly
#

No clue what that is and I'm already puking

late smelt
#

It’s a programming language

#

Pretty old

dense leaf
#

pretty shit

junior holly
#

Damn it only made it to 6.0

late smelt
#

Visual Basic .NET only made it to 6.0

#

Visual Basic is still alive, and is on version 17

junior holly
#

Oh

woeful pasture
#

oh you posted internals xD

woeful pasture
junior holly
#

mmmm

woeful pasture
#

this could much better be done with some sort of registry and more functional approach

junior holly
#

Funny you say that cuz last time me and con talked, he said I was doing "too much functional" stuff kek

junior holly
woeful pasture
#

if skills apply changes to how the disc flies, why not apply those transformations as functions? It only makes sense

late smelt
#

Did someone say registry

#

😩

junior holly
woeful pasture
#

yeah I feel like not applying the skills as functions makes no sense here 👀 especially since they can be modeled by pretty simple functions

dense leaf
junior holly
#

I think my biggest thought was keeping serialization separated but then again why put each skill into it's own file, could just toss them all into one call it a day

late smelt
#

I need to make a registry system like mojangs

junior holly
woeful pasture
#

just my thoughts, I prefer using functions in this case over objects so just my opinion but ye

junior holly
#

I mean I could the same with internal functions within the skill classes

#

Idk I like the deep encapsulation too

alpine anvil
woeful pasture
junior holly
#

I feel like skills aren't something that should ever be messed with outside of their current and max levels

woeful pasture
#

I never saw any utilitiy of it over something like mine which is just a thin map wrapper with utilities prety much

junior holly
#

And perhaps creating your own skills too... not sure on that one yet

late smelt
#

Yeah but is yours statically accessible

woeful pasture
late smelt
#

All of mojangs registries are static

#

Stuff can also be registered to them in static fields

woeful pasture
#

I always have a Registries class so I can do Registries.THING

junior holly
wintry fern
#

The other issue with these is when using them on stuff you cant guarantee wont be null later and then it throws trap errors

#

Trap if you dont know is a type of error where you stated in your code that it wont be null or is not null and it ends up being null later lol.

cobalt trellis
#

Idk, I had relatively few issues with that method. On the other hand I understand what the implications of it are

hasty lotus
#

i honestly use it to suppress ide nullability warnings if i know the object is guaranteed to be not null if that makes sense (sometimes IDE's aren't smart enough to tell)

#

if it is null then its useful cause it tells me before breaking more stuff

#

and id just import and use the static method so it would be like

final Player player = requireNonNull(foo.bar());
late smelt
#

Just use nullability annotations smh

random yacht
#

Or assert if you wanna be really fun

hasty lotus
#

i usually use checker so i dont have to add in the annotations

#

cuz it adds them in for me at compile

random yacht
#

I do actually use asserts for some calls, like to ItemStack#getItemMeta() if I just freshly made the ItemStack. It's never going to be null

#

And assertions are disabled by default so whatever. I'm only doing it to appease my IDE

hasty lotus
#

i do the same but use requireNonNull, but requireNonNull is basically a useless null check at this point

random yacht
#

Objects#requireNonNull, imo, is for streams

hasty lotus
#

People who use Preconditions

round fossil
#

implicit null checks do be lovely at times, altho it can get rly anti pattern-like

late smelt
#

Muh cpu cycles

sharp epoch
#

as if anyone coding in Java cared about that

#

nowadays most of algorithms are more memory bound than cpu bound anyway

night knoll
#

xd

#

ajajaj

late smelt
#

Muh bytes

round fossil
#

shame we always have to sacrifice something

#

zero cost abstractions when

late smelt
#

If your code can’t run on an Atari 2600

#

It’s bad

junior holly
#

Changed this from player.getUniqueId to the id I pass... I was being dumb lol

round fossil
#

its a bit weird

#

but ehm well for starters

#

why not depend on Plugin instead of JavaPlugin?

junior holly
#

I assume other people might want to use my round objects

round fossil
#

and if u use ConcurrentHashMap, your type should be ConcurrentMap right?

#

since the concurrency becomes a requirement rather

junior holly
#

Fair

round fossil
#

Plugin is the interface of JavaPlugin

#

so why JavaPlugin over Plugin?

junior holly
#

Oh well then because I wasn't aware of that lol

round fossil
#

na all good, just curious

junior holly
#

What about the handlethrow method, do you feel it's redundant to have multiple handlethrow methods per object?

#

ie: One in the round as well as the disc object

round fossil
#

oh

#

eh it depends

junior holly
round fossil
#

i mean it becomes ambiguous

#

like which is the right type to invoke the method on, and which type is just the intermediate one

#

alternatively name them better

junior holly
#

Maybe the round method should be called handleStroke

#

I feel that'd be a bit better

round fossil
#

yea i mean maybe :>

woeful pasture
#

shouldn't the handleStroke call be under the Hospital's ICU object

junior holly
#

Haven't even implemented teh ambulances yet kek

woeful pasture
#

considering you're from America you might be best off implementing an Uber class instead

junior holly
#

Nah health insurance system is already functional

#

Just don't be dumb and not have insurance

woeful pasture
junior holly
#

Well I was thinking, from the context of the round I should invoke a player object just for those odd cases a player might log out, since the disc object's handleThrow method actually uses the player object

round fossil
#

also kat

#

RoundHandler

junior holly
#

I just mean handle the nullability case for the player in the round method rather than the disc method

junior holly
round fossil
#

why is only the map thread safe

#

and the sets are not

junior holly
#

Because I am dumb

#

I forget that's a thing psh

round fossil
#

fair

#

well u could also ask urself if u perhaps are oversynchronizing rn

#

cuz just smacking thread safe collections on everything tend to lead to oversynchronization

junior holly
#

I just thought most times people like to handle their "rounds"/minigames async so

#

Thread safety is a concern most times in the data migration innit?

round fossil
#

that's a nice thought, but in this code u dont deal with async whatsoever

junior holly
#

yeah true

round fossil
#

well read writes happening at the same time

#

and writes that depend on reads

#

ofc oversynchronizing is better than undersynchronizing, but as said

junior holly
#

erm concurrency is gonna take me forever to fully understand

round fossil
#

u should primarily focus on writing a structure that follows what you need, you can let your "library consumers" derive your types (<-- open closed principle) and pass their own dependencies to concrete types to allow of modification

#

i think there's a principle for this

#

its called open-closed principle if im not mistaken

junior holly
#

I will have a read on it for sure

round fossil
junior holly
round fossil
#

yea

#

if concurrency is a requirement

#

then def add that

junior holly
#

mmm

round fossil
#

and this principle is called liskovs substitution principle i believe xD

#

well i havent done high level software for some time

#

but ya

junior holly
#

Thinking about it more the only "hardcore" math that occurs is the disc physic modifications, still this has no relation to concurrency needs of the round, any other things that have to deal with this data is simple math and other functions

round fossil
#

eh depends

#

it might

#

but prob not

junior holly
round fossil
#

it doesnt even look that bad

#

or at least u made it out to look nice

junior holly
#

Well I do certainly appreciate that

#

backhand and forehand physics are quite similar

round fossil
#

Well I do think u should avoid Math.random()

junior holly
#

basically just swap the +/- gives it the flight path irl

round fossil
#

but apart from that

#

nice

junior holly
#

either +/- on those too

round fossil
#

yea anyway kat

#

for this kind of code, it can be fine to leave some comments on it

#

explaining what the math is doing yk

junior holly
#

yeah tbf there is a single comment in that whole thing cuz I was trying to explain to a friend (not a coder)

#

There were more but they are dumbed down lol so I removed them

#

Also a lot of the math needs to be converted to configurable fields instead of magic numbers

round fossil
#

yea

junior holly
#

Did you ever see the irl reference compared to how it looks now?

round fossil
#

nope

woeful pasture
# round fossil nope

unless I missed something newer here's the comparisson
#general message
#general message

pine grail
#

A)

public void handlePlayer(Player player) {
  if(player.getHealthPercentage() < 0.50) {
    player.setHealthPercentage(0.50); // Sets the health to be at least 50% of the maximum health, convenience method
    player.sendMessage(Text.literal("Healed to 50%").color(Color.GREEN));
    return;
  }

  player.sendMessage(Text.literal("You have more than 50% health!").color(Color.GOLD));
}


public void otherMethod(Entity entity) {
  this.stopTicking.add(entity.getUniqueId());
  this.spin.add(entity.getUniqueId());
}

public void someEventHandler(EntityTickEvent event) {
  if(this.stopTicking.contains(event.entityId())) {
    event.cancel();
  }

  if(this.spin.contains(event.entityId())) {
    var storage = event.entity().storage();
    var plugin = event.context().plugin();
    var id = Identifier.plugin(plugin, "ticks");
    var ticks = storage.getInt(id, 0) + 1;

    if(ticks >= 3) {
      spinEntity(event.entity());
      ticks = 0;
    }

    storage.setInt(id, ticks);
  }
}

vs

public void handlePlayer(Entity player) {
  var health = player.get(Health.class);
  if(health == null) return;

  var messages = player.get(Messages.class);
  if(messages == null) return;

  if(health.getPercentage() < 0.50) {
    health.setPercentage(0.50); // Sets the health to be at least 50% of the maximum health, convenience method
    messages.send(Text.literal("Healed to 50%").color(Color.GREEN));
    return;
  }

  messages.send(Text.literal("You have more than 50% health!").color(Color.GOLD));
}


public void otherMethod(Entity entity) {
  entity.remove(Ticking.class);
  entity.add(new Spinner(3)); // Every 3 ticks
}

public class SpinSystem extends System {
  // Implement this yada yada yada
}
#

I guess there is also ensureGet, basically Entity#ensureGet throws an exception (with a custom message if provided) if the entity doesn't have that component

#

Please if you can state the reasoning (cc: @dense leaf)

night knoll
#

ECS way better

pine grail
#

I suppose I did not ask for a detailed reasoning

night knoll
#

Is ECS code real thing in spigot or that was pseudo code?

pine grail
#

it's not Spigot-specific, it works as a sort of translator to multiple platforms, I suppose

pine grail
night knoll
#

Yup

dense leaf
pine grail
dense leaf
#

Also it'd be nice if I could just access basic player data like health and saturation in the entity object

pine grail
#

The Entity itself is not a Player, nor a zombie nor a wither, it's just Any entity

#

An entity could very well just be a block

dense leaf
#

...What

#

Why would an entity be a block

pine grail
#

A block has components:

Touched
Landed
etc

#

or I suppose not

dense leaf
#

A block is certainly not an entity tho

pine grail
#

It's an entity in the sense of a presence in the world, not a mc entity, but I can limit it to be only entity entities for now

#

Also

#

regarding what you said

dense leaf
#

So I'd just rename it to ECSEntity or something then since that can get confusing quickly

pine grail
#

I mean why would I do that? You don't depend on any other libraries

#

a block would also be part of the ecs

dense leaf
#

I mean do you not have an actual entity entity class then

pine grail
#
Entity entity = ...;

entity.sendMessage(Text.empty());

entity.sendMessage(new FireDamageMessage(5));
pine grail
#

each entity is defined by it's components

#

example

dense leaf
#

Entity entity as in actual minecraft entity

night knoll
#

Bro didn't used game engines before 💀

#

Entity is literally any game object

pine grail
#
var zombie = ...;
zombie.add(new Position());
zombie.add(new Velocity());
zombie.add(new NetworkIdentifier(Identifier.minecraft("zombie")));
zombie.add(new Health());
zombie.add(new Pathfinding()); // or something
#
var slimeBlock = ...;
slimeBlock.add(new Position());
slimeBlock.add(new NetworkIdentifier(Identifier.minecraft("slime_block")));
slimeBlock.add(new Bounce());

// you can technically now add the block in the world
#

Obviously this process is not done by hand

night knoll
#

Idk looks like library for minestom

pine grail
#

Yeah the goal is providing the same flexibility on the vanilla server (ig Spigot / Forge aren't really vanilla but ykwim)

dense leaf
#

I saw it yes

pine grail
#

What do you have to say about it

dense leaf
#

idk I'm not really a fan of ECS's

pine grail
#

Why so

dense leaf
#

because muh classes

#

I just wanna zombie.setHealth(20) or something

pine grail
#

zombie.get(Health.class).set(20);

#

plus you can also remove health after that

#

make the zombie immortal

night knoll
#

Make your own health component

pine grail
#

^

#

You can add whatever functionality

night knoll
pine grail
#

oh fair yeah rad could just use extension functions 💀

junior holly
dense leaf
pine grail
junior holly
pine grail
junior holly
#

oh kek

random yacht
#

Looks like some shit Sponge would conjure up

#

I still think Java's a shit language to do component-based paradigms. Languages with templating and reified generics are way better to do it with.

woeful pasture
#

its actually so great cuz then you don't need inheritance! I'm sure it is not confusing design at all!

random yacht
#

Kotlin can eat a dick >:(

woeful pasture
#

what is reified generics exactly?

random yacht
#

Compile-time type information from generics, basically

#

Java has type erasure

woeful pasture
#

ahhh

alpine anvil
dense leaf
sharp epoch
night knoll
#

-# Heart attack warning

dense leaf
pine grail
#

think about it

#

You can have both!

#

(/hj)

dense leaf
#

DFU would benefit from templates A LOT

desert ore
pine grail
#

which says Entity, Block, etc

pine grail
#

good call @desert ore btw

#

(also if anyone can further vote I would like a more definite answer)

desert ore
#

I'd be interested to see a system that is completely and fully component based, maybe even

var server = context.getServer();
if(server.get(Type.class) == null || !server.get(Type.class).is(Server.class) || server.get(Logger.class) == null) return;
server.get(Logger.class).info("hello world");
``` but that seems annoying
pine grail
#

it is very much annoying

#

the system is component based for the following:

Entities, Blocks, Items

#

Then

#

Lifecycles are fully plugin-driven except LOAD, ENABLE, DISABLE, UNLOAD

#

Events follow in suit

#

except vanilla events

#

which are also built in

pine grail
#

I mean that is WILD and I won't allow it

#

mainly because of structural reasons

round fossil
pine grail
#

I took some liberties to make sure no code that runs on this system is capable of affecting anything outside of its own VERY limited area, not even other plugins, it all communicates through events

pine grail
#

Anyways, do I proceed with the component-based approach?

desert ore
#

In some way, everything being runtime based is even pretty good

round fossil
#

I mean both have different pros and cons

pine grail
#

Yeah, I like the component system for it's flexibility and adaptability

desert ore
pine grail
#

You know what, this is my framework

#

fuck it

night knoll
#

Fuck around and find out

pine grail
#

Yeah

#

And plus it will allow me to make some very funny things

#

The trouble...

round fossil
pine grail
#

It will work on Fabric easily

#

Spigot on the other hand...

#

ugh kill me

pine grail
#

I definitely want something cleaner than THAT

desert ore
#

I like the concept of sponge, but I'm not sure whether I like the implementation of it that much

round fossil
#

they have different implementations right?

#

I mean it may make more sense to go with a componential design then

desert ore
#

I believe yeah, but today we were talking about sponge and somebody brought up the itemstack builder

#

That shit is hell

pine grail
#

how should I add duplicate component add

#

return false? return the old component? throw an error? add both?

#

what if I add an "allowsDuplicate" flag on the component

sly jackal
#

what is duplicate component add

desert ore
#

Maybe a seperate component to store a list of a certain component

sly jackal
#

i dodnt follow the convo, what kind of component is this

desert ore
desert ore
sly jackal
#

how are those components even stored

desert ore
#

Probably a list or a hashmap

#

Map<Class<Component>, Component> seems reasonable

sly jackal
#

well if its a map, dont allow duplicates

#

easy as pie

desert ore
#

Ye, but I think a ComponentListComponent would be interesting

sly jackal
#

well yes you would have to

#

otherwise how can you even keep a list of things

desert ore
#

True

#

Ah I love the idea of components

sly jackal
#

pretty much sounds like you are trying to do nbt with extra steps

desert ore
#

And I love it

#

I absolutely fucking love it

sly jackal
#

what are the components even gonna be used for

desert ore
#

everything

#

Entities, blocks and everything of that type ig

sly jackal
#

oh, well too bad everything is already a component

#

by the name of Object

desert ore
#

Lol

dense leaf
#
inline fun <reified T : Component> Any.get(): T?

frfr

pine grail
#

It is used for example by Unity DOTS to allow for dozens of thousands of entities ticking at once basically

desert ore
#

Damn

#

Can you apply a ticking component to a non-ticking entity?

pine grail
#

Also ECS is pretty much the only way you'll get more than a few thousand entities at once without heavily multi threading or pulling other tricks

#

It can handle thousands upon thousands of entities (even in the 100k+ range) *if implemented correctly

#

Right now I won't implement it directly in the game, more or less slowly replace vanilla with it

hasty lotus
#

More specifically, this https://github.com/PulseBeat02/MurderRun/blob/main/src/main/java/io/github/pulsebeat02/murderrun/game/gadget/GlobalGadgetRegistry.java. I am loading and instantiating all my Gadget classes which I store in an Enum. I first load them into a registry, where their String identifier and ItemStack item in inventory can be just first used for display purposes. But each Gadget has its own function and utility when the game starts. When the game starts, I create a new instance of all the Gadget to make sure it doesn't conflict with any other games that may be running at the same time.

#

But does anyone know a way to "lazily" load the Gadget I only need at runtime. The thing is, one of my Gadget is a RandomGadget, which is a gadget that gives you a random gadget, and that means I'd have to load the Gadget class when it's activated. Anyone know a good way to lazily load the Gadget classes so I don't have to load hundreds of them each time the game starts to save resources?

dense leaf
#

nice code

hasty lotus
#

Bleuh

late smelt
#

How does an ECS allow for thousands of ticking entities at once

woeful pasture
round fossil
#

and java has reified generics, just extremely awkward and uncomfortable to use

dense leaf
jagged rock
#

where you can get the Class<T> back from the empty array

desert ore
#

I wonder how different things would be if java didn't have type erasure

sharp epoch
#

type erasure is a good thing, people just go bonkers over the type system otherwise

sharp epoch
pine grail
# late smelt How does an ECS allow for thousands of ticking entities at once

well basically, take minecraft: Each entity is processed for something every. single. tick. Even if it can't actually do anything, in an ECS instead each tick there is basically a bunch of queries that are ran (can happen in async), so things that run in tick intervals just get ignored until they should run, and don't count towards any loops (as they are separated by archetypes, basically a set of components)

#

TL;DR: Just separating entities into buckets and allowing for parallel processing of entities (i.e. all villagers will tick together WHILE all of the zombies are ticking together)

#

entity <-> entity interactions are scheduled at the end to ensure synchronization

#

iirc

jagged rock
#

That's if the ECS you're running is concurrent yeah

pine grail
#

I mean yeah I'm doing that

#

sort of

#

it will be concurrent

#

notrn

#

Right now I'll just do a translation layer (so performance--) but later I'll slowly replace vanilla stuff with it

desert ore
#

You are planning to make it non-vanilla and incompatible with everything?

pine grail
#

oh wait

#

Hm

dense leaf
#

lol

pine grail
#

If you're using something that fundementally changes how plugins/mods are made

#

Should you really be expecting that everything else works with it

#

custom entities will work

#

mods that change vanilla ones won't

round fossil
# jagged rock isn't it the T... hack

I mean just T extends S on a class type parameter (though u may still lose higher ordered types), and then also you got the type token hack with TypeToken<T>{} (subclassing and passing ur type to the superclass type parameter)

#

But yea varargs can be polluted w non reifiable params

desert ore
pine grail
pine grail
# pine grail
poll_question_text

Which block do you prefer?

victor_answer_votes

3

total_votes

4

victor_answer_id

1

victor_answer_text

A (Spigot-ish way)

#

well then it is ECS

dense leaf
#

?

sharp epoch
#

blud just wanted to talk about ECS

woeful pasture
#

Guys what's an ECS

dense leaf
#

extra complicated system or something idk i don't do gamedev

woeful pasture
dense leaf
#

yeeah

woeful pasture
#

I wonder if mojang will ever embrace entity components like they do items

sharp epoch
#

it wouldn't be exactly in the way Ike demonstrated though, at least I don't think from looking at the current source changes

pine grail
willow bone
#

you can essentially add any <T> as a component for the entity

#

and also get it back by giving an entity id

#

it saves 'em in a sparseset

pine grail
#

Tbh that's a neat idea but not for me

willow bone
#

What you planning on doing then?

#

oh wait let me show you my query system too

#

it's clean as fuck

pine grail
# willow bone What you planning on doing then?

Entities aren't builders, they are constantly modifiable, replaceable, they can cross universes and are not bound to one system, each entity is given n archetypes depending on how many archetypes it matches, it may match two identical archetypes, each archetype has a priority and a system attached to it

willow bone
#

yes ofcoruse

pine grail
#

This way you can have THE maximum flexibility

willow bone
#

the first image I showed is how to create an entity

#

let me show you how to query and modify an entity for example

pine grail
#

And you rarely interact with the components

willow bone
#

me or like in general

pine grail
#

Me*

#

Basically: send a text message to an entity, if it can process it, it will! Otherwise it won't. But you don't know.

#

Send a damage message

#

Did it get damaged? Probably

#

What's the health? Does it even have health?

#

Zero coupling, even between the things acting on the entity and the entity

#

The entity also doesn't know which messages it can receive

#

Each component says what it can receive

willow bone
#

what is an 'entity' in your case?

#

in my case, it's just a usize (u64 / long?)

#

@pine grail Example of query

#

the "query" gets all entities that have the player identity + position component

#

this can be any amount of components and any type

#

since I need this all to be multithreaded, there cannot be 2 multiple mutable borrows, but infinite immutable ones

#

this supports 100% multithreading

pine grail
#

Otherwise it's just a number to a list of components

willow bone
#

do you save entity -> Component[] or Component -> entity[]?

#

arrays of structs or structs of arrays

willow bone
pine grail
#

Or actually

#

Yeah there that's it

willow bone
#

oh so each entity manages its own components?\

pine grail
#

Yup

#

Fast lookup

willow bone
#

I have HashMap<Type, SparseSet<usize, Component>>

#

because in systems, you don't care about individual entities

#

you care about the general population

#

so it'll be better for CPU cache this way?

pine grail
#

Yeah that's fair enough but then the issue arises of accessing speed, in my case each entity is given a unique archetype ID, this ID is then used to instantly know every entity in the universe without doing any further lookups

#

I.e. the accessing speed to get every player is O(1)

#

Same for every say zombie or zombie-like entity

#

Etc

willow bone
#

hmmm interesting

pine grail
#

You straight up ignore the cache because the arrays are also contiguous in memory, as each component is an ID into the global component table

#

Act no that was the old one

#

Current one doesn't have a global table anymore (faster)

#

But still yeah all of the data is thightly packed together

pine grail
willow bone
#

good luck with your idea. you're doing it for minecraft right?

pine grail
#

The state is actually in the entity for me, you do entity.get(Health.class)

willow bone
#

wont that take way more memory?

pine grail
#

Nop

#

I mean "in"

#

That's not quite true

willow bone
#

even if you have a atomic shit, it still saves it

pine grail
#

It's in the universe but the entity knows what it is from an id

#

Basically an entity is just two IDs

willow bone
#

heh

pine grail
#

Considering that I could hold 100k entities in around 1MB of ram it's not too bad

#

Java ram overhead go brrrr

willow bone
#

mine takes WAY more

#

my rust skills aren't the best

pine grail
#

basically the issue is

#

each object has some overhead

#

around 32 bytes of ram iirc

willow bone
#

how 100b per entity

#

with or without components?

#

like an empty entity?

pine grail
#

it would be entities with position and maybe another component such as health

#

yeah no you could store that in 100b per entity

willow bone
#

position is around 24 and health around like + another 4, so 28 bytes

#

do you support multithreading?

pine grail
willow bone
#

each component has 32?

#

so 60?

#
  • 32 of an entity
#

so around 100b?

pine grail
#

actually no I'm dum

#

That is for arrays

#

assuming you're on a 64 bit system

#

entities are 48 bytes, each component is 16 + data, so in this case it would be 32 for position and 20 for health, so exactly 100 bytes

willow bone
#

interesting

#

mine would definitely take way more

late smelt
#

Smh integer health

willow bone
#

float better

pine grail
#

Fixed precision numbers

willow bone
#

isnt float a 4 byte

#

thing

pine grail
#

I mean yeah I guess

#

Fair enough sure you can just use a float, doesn't change the memory usage

#

Though there is a downside

#

there is inherently some more storage to take into account to hold the archetypes

#

And to hold the components

#

it ends up not being 100k entities, it would be 100k entity objects @ 100 bytes per, but then there is collection overhead

willow bone
#

12.3 MB for 1 million plain entities

#

tf

#

12 bytes per entity?

#

8 bytes for the entity itself

#

and 4 bytes for uh

pine grail
willow bone
#

let me try with position

pine grail
#

mine would be around 46.8kb for 1M plain entities

willow bone
#

88 MB with position

pine grail
#

Plus collection overhead but idk how to calculate

#

I can actually lower this by using the entity object itself as the entity ID

#

because java already gives me a unique ID per object which is part of the 16 bytes

#

removing 32 bytes ish

#

around 15.6kb per 1M plain entities if I do that

willow bone
#

82 bytes per entity with a position (double*3) = (10 million entities took 808 MB ram)

willow bone
#

do you support multithread?

pine grail
#

It would be insanely hard to support multithreading in mc

#

but it does support regional collisions

#

and regional entity lookups

willow bone
#

wtf

#

regional entity lookups?? how?

pine grail
#

So basically entities are ran through the position system, this system basically sorts entities into an octree so that you can access an area extremely quickly and disregard the rest of the universe

willow bone
#

octtree takes a shit ton memory no??

#

last time i tried on a 100k or a million entities and it took almost 16 gb ram

pine grail
#

An octree is either nullptr or pointer to either another octree or an entity index start - entity index end numbers

#

each octree element is 16 bytes

willow bone
pine grail
#

basically

#

16 * 1.000.000 / 1024

willow bone
#

that'd be 15625 (kb)?

#

or 15 megabytes about?

pine grail
#

hmm

#

lemme thonk

#

Ah yeah I'm DUMB

#

it's 15MB

#

or 68MB per 1M entities with pos and health

willow bone
#

160 for me

#

yikes

pine grail
#

though I can lower this

#

considering that mc only uses 8 bytes for position

#

I can do the same and actively edit the number

#

and then that would bring it down to

#

60MB

#

not bad?

#

also the issue here is using two diff components btw

#

I FOUND A SOLUTION

#

@willow bone

#

I turned 100M entities into just 36MB with pos and health

late smelt
#

How does mc use 8 bytes for position

pine grail
#

sacrifices the y axis

late smelt
#

Eh?

pine grail
#

y is -2048 to 2047

late smelt
#

How does that work for stuff at decimal positions tho

pine grail
#

¯_(ツ)_/¯

#

actually

#

mc uses deltas to signify position

#

they send 6 bytes per delta

#

and uses 24 bytes per teleport

#

so actually

#

still 12 bytes

#

40MB

#

eh

willow bone
#

100m is 100 mb

pine grail
#

wait NO I FORGOT ABOUT JAVA OVERHEAD

#

KMS

#

wait no that's not in the entity

#

poggers

willow bone
night knoll
#

plz

sharp epoch
night knoll
#

gracias

#

the fact that you literally update it is so crazy and says much about you

willow bone
#

i think its this one

night knoll
#

ty

willow bone
#

np enjoy

night knoll
#

ty

#

you 2

dense leaf
empty wigeon
#

is this a fancy way of checking if some number of players are ready before some action can proceed?
what are we getting ready for? 🐈

dense leaf
#

we love kotlin

empty wigeon
#

anyway, the first thing that stood out to me is ChatListener and how it's getting an instance of the plugin just for registration, which could be moved to the main class itself via some dedicated method that accepts registerEvents(Listener... listeners) or something like that ¯_(ツ)_/¯

sly jackal
#

i love a good plugin class singleton

#

but I do agree on passing the plugin instance in your chatlistener instead of getting from your main class yourself

#

which is just coupling that isnt needed

dense leaf
#

singletons my beloved

dense leaf
sly jackal
#

i would just make it a variable passed in the comstructir

#

idk like maybe in the future you want this listener to check other stuff about a plugin like getting a resource or smth

#

also really nitpicky but in the ready command I would put the failing conditions at the top, not at the bottom

#

which in this case would make it an early return

dense leaf
#

yeah true

dense leaf
#

I just realised

#

I named the class RecollectPlayers instead of RecollectPlayersCommand

dense leaf
#

I register perms in my plugin.yml if that's enough for stuff like LP

alpine anvil
#

perms work without needing to be Permissions afaik

alpine anvil
dense leaf
woeful pasture
#

Epic is in denial about fluent getters

#

I always use them for statics lol

alpine anvil
#

fluent getters are for kotlin and records only

#

and paper

dense leaf
round fossil
# alpine anvil fluent getters are for kotlin and records only

The only real argument I’ve heard against fluent command and queries is that with get/set you can prompt your IDE to list all the “traditional” getters and setters (suggestion+/completion), and that it’s technically stricter by specification than fluent command and queries

sharp epoch
#

it is really not that big of a deal honestly

#

maybe because I'm used to look at the javadocs instead of depending on the auto-completion I guess, even if I get all the getters by doing get, often times I want to read what the get these do as well

lyric stump
dense leaf
#

this is the reason I hate bukkit commandexecutors

desert ore
round fossil
sharp epoch
#

I was hoping you'd notice the pun in my message lol

round fossil
#

i prob missed it :')

#

so I assume you are pro-fluency then?

lyric stump
round fossil
#

Why did you suffix it optimized? Out of curiosity

alpine anvil
#

its super asnyc optimized deluxe async perforamce ++

dense leaf
#

multithreaded

lyric stump
#

It's a joke just in case you didn't get it

sharp epoch
#

but I do like consistency if anything, so mixing up fluent getters/setters with non-fluent ones is a no-go for me

#

there are cases where it can't be helped but if you can avoid getting inconsistent naming then better

random yacht
#

I assure you it won't be lol

#

At least with regards to this specific problem. You still have to specify command arguments and what they execute

#

If you don't want to be passing through your messagesConfig and messagesManager to these methods, make them fields or something. Or get them in the methods

dense leaf
#

Well obviously yeah but it's just worse with Bukkit

#

Brig is much better

random yacht
#

Oh okay Bukkit is just objectively bad for something Brig does the same KEKW Gotcha

dense leaf
#

I mean

random yacht
#

If you look at a vanilla command implementation, it's pretty similar, only it uses a builder pattern instead of if/elseif

dense leaf
#

At least you don't need to manually parse with Brig

random yacht
#

Yes but here specifically you're complaining about the if/else chain

#

So >:( Be specific when you complain

dense leaf
#

hehe

#

I just hate Bukkit CommandExecutors in general, had to use em recently

#

Except that I didn't need any argument parsing luckily

lyric stump
#

Lucky

dense leaf
#

I usually use Cloud for plugins, but Brig is also amazing

hasty lotus
#

I used to use Brig but I switched to Cloud

#

I use annotations because I really don’t like writing command argument parsing code or anything

#

I just want to write the damn function of the plugin, I don’t want to write command argument parsing logic!

sharp epoch
# random yacht At least with regards to this specific problem. You still have to specify comman...
Commands.register(literal("idk")
                .then(literal("reload")
                    .executes(this::reload)
                )
                .then(literal("give")
                    .executes(this::give)
                )
                .then(literal("delete")
                    .executes(this::delete)
                )
                .then(literal("reset")
                    .executes(this::reset)
                )
                .then(literal("migrate")
                    .executes(this::migrate)
                )
                .then(literal("open")
                    .executes(this::open)
                ) 
 // help not needed anymore considering the client will suggest proper args ?)
        );

it looks horrible no matter how you refactor it, I'll give you that

dense leaf
#

In Kotlin you can remove the this and the semicolon

#

way better ikr

random yacht
#

I'm not sure that is better lol

#

But yes, Javier gave a good example. You're going to end up with what is essentially an if/else, only it does it with a builder

dense leaf
#

Command registration is always gonna be a PITA

sharp epoch
#

I believe that can easily be solved by making a DSL

dense leaf
#

Cloud!

#

Kotlin DSL!

sharp epoch
#

Cloud is eh, I always felt it is overkill for anything simple, just too much engineering going on there

dense leaf
#

That's true but that's also great for bigger and more complex commands

sharp epoch
#

don't know what their v2 is looking like right now though, has been a while since I looked at it

junior holly
sharp epoch
#

eh, frameworks like cloud exist for that very reason

junior holly
#

Why is everyone so keen on using the wheel instead of reinventing it smh

sharp epoch
#

their annotation-based stuff is essentially for those who just have a very declarative command tree and want to center on the actual command logic

#

||this is why I just use Skript for commands||

hasty lotus
#

the frameworks exist to minimize that

round fossil
#

the feeling when you write a command abstraction for the fifty-eleventh time

sharp epoch
#
round fossil
#

🥲

night knoll
#

playerkits 2 is good

#

ajneb's is better

lyric stump
#

Also, I'm learning so it's a win for me.

night knoll
#

mhm

night knoll
#

you guys wouldn't mind if I ask you to review my lib which uses both minestom and paper?

hasty lotus
#

Put it here

night knoll
#

I will put in a bit I saw some small issues need to patch it up quick

lunar vale
lyric stump
#

U don't think so I did that

#

I

lyric stump
#

Lmfao

#

Me no english

#

LOL

#

And yeh it's probably in the original player kits 2

late smelt
#

Looks legit to me

sleek shard
#

I'd take it

#

lemme become a skript kiddie

#

I'm gonna hack em
tree

pine grail
#

wait no

#

yeah no that's correct

sleek shard
#

yeah I mean the 1.8 color codes are just default colors on windows cmd

pine grail
#

lol

hybrid osprey
#

Theyre the CGA colors

dense leaf
pine grail
#

that couldn't've happened to me because we don't have computers in 3rd grade kekw

dense leaf
#

that was like 10 years ago or something, don't remember what age I was in 3rd grade

pine grail
#

yeah so around 6yo for me then, which again, we only had computer lab next year?

#

and it was mainly just scratch

dense leaf
#

We had to go there for learning how to use a mouse and shit

pine grail
#

lmao

sleek shard
#

was fun tho

woeful pasture
sleek shard
#

ahahah

woeful pasture
#

Like I had IT people walk behind me whole I was coding on Pycharm on an arch OS

sleek shard
#

in secondary/high school I also managed to bypass their security and got admin on one of the pc's... could do literally anything

#

shouldn't be saying that but anyway

woeful pasture
sleek shard
#

LOL

woeful pasture
#

It's funny to read

sleek shard
#

literally on the pc I managed to replace the shift lock with cmd (windows 7, back when I was around 13-14), opened up cmd on the logon menu, did net user, and set myself as an admin

#

that's the pc I got admin on

woeful pasture
#

Tbf you got security problems of the bored autistic kid hacks in on a whim

sleek shard
#

🤣

woeful pasture
#

You could run a lot on my school computers cuz they had python on em

#

Anyone with an ounce of knowledge could do a ton

#

Wasn't secured or anything

sleek shard
#

wtf

woeful pasture
#

I was able to change env vars and registry access from python

woeful pasture
sleek shard
#

nah deadass

#

in my college (a week ago) I literally just made such a simple program into a class, methods with returns, params, and in/out stuff. Literally basic shit. And my programming teacher was impressed 🤣

#

c++

woeful pasture
sleek shard
#

oh hell nahhh 😂

#

I love that

woeful pasture
#

I got 100% on it too even though one of the requirements was the code being concise. I will tell you when the class is 500 lines of reflection and the top is like

@Input("Enter One Number")
@While("number", Integer.class)
@Input("Enter A Second Number"_
...
#

If I were to take a class right now I'd probably write everything in bytecode using ASM lol

#

i'd just bootstrap my jar so they could run it

sleek shard
#

honestly one thing is - I've been doing java since I was around 12, but I do say 6 years because I like to keep it humble, as I didn't really start learning proper shit until I was 13-14. 20 now, so the route of programming is going quite well

woeful pasture
sleek shard
#

I mean even now I can't even tell you every nook and cranny about java

#

only recently I've gotten good at it

#

within the past 2-3 years I've actually been trying to get better

woeful pasture
#

I've always been great, guess its my inate talent to make Inventory PRs

sleek shard
#

😂

dense leaf
#

I've only been using Kotlin for about a year now but can tell you about I'd say 80% of the languages features

sleek shard
#

I mean I think I know every java keyword

dense leaf
#

oh so you know java? name every class name

sleek shard
#

public - obvious
private - obvious
protected - makes it accessible to sub-classes for inheritance
blank - makes it accessible to package-private, allowing only the classes inside of the package allowed to access it
volatile - make the (primitive) data type visible with updates to all threads
... and some more

night knoll
sleek shard
#

not every one but yknow

#

😂

#

I just don't have time to list them all out

#

and kinda cba

round fossil
#

I meaaan volatile prevents compiler optimizations also

sleek shard
#

oh that's a new one

#

thank you mr conclube

round fossil
#

:>

late smelt
#

What about strictfp!!!1

hasty lotus
#

native

round fossil
#

non-sealed :D

hasty lotus
#

sealed

random yacht
#

const!

round fossil
junior holly
dense leaf
#

Since you're already using paper, they have ItemStack#editMeta

#

I usually just do something like this so the scope is this and it returns the stack again: ```kt
inline fun ItemStack.editItemMeta(block: ItemMeta.() -> Unit) = apply { editMeta { block(it) } }

inline fun <reified T : ItemMeta> ItemStack.editItemMetaSpecific(crossinline block: T.() -> Unit) = apply { editMeta(T::class.java) { block(it) } }

spring reef
#

i would want to see that disc flying, rather than lots of particles blocking the view

#

codewise i would trim down the methods, reduce boilerplate and put the complex equations into a descriptive method

#

driver and disc are a bit misleading. i would not know what that should be without looking at the code

#

personally i would turn ThrowTechnique into an interface, and then make an abstract BaseTechnique or AbstractTechnique which then is the base for further techniques

dense leaf
spring reef
junior holly
#

Elaborate

#

This is a disc golf mini game system so I don’t understand what the heck id do with combat

random yacht
#

I'm also not sure any of those suggestions are really beneficial lol. You could maybe compact down your switch case with a more generic method to reduce duplication but that's really about it

#

The terminology of those classes is fine. The "Driver" is a type of disc. Akin to how there's a "driver" type of golf club

#

I also don't think the throw technique needs to be overcomplicated into an interface. It's just not necessary. Abstract class is fine

junior holly
random yacht
#

If you want to get super critical you could maybe argue you could rename Driver to either DiscDriver or DriverDisc, whatever. Some people (myself included) like to link types of objects together by name

#

That's preference though tbf

#
private final String technique;

public ThrowTechnique(String technique) {
    this.technique = technique;
}

public String getTechnique() {
    return this.technique;
}

Also not entirely sure why you're passing a "technique" string to a throw technique. Is this better named as "name"? Or "description"? Or is it something else?

junior holly
#

Registry key

random yacht
#

Yeah then maybe "id" or "key" would be better imo

junior holly
#

Sure sure

random yacht
#

Feels weird passing something called "technique" to a technique object :p

junior holly
#

Yeah no for sure lol

random yacht
#

Other than that, code looks fine. Like I said, you could maybe compact down your switch so that each block just calls a more generic method, but I wouldn't even stress too much about that

junior holly
#

Mmm maybe, there’s a lot of techniques so that switch is gonna be duplicated a lot

#

Though the physic manipulations will be different

random yacht
#

Yeah, was writing out a generic method, kinda sucks ngl lol

#

It's fine

junior holly
#

kek

#

Visually speaking, what do we think?

#

(I pinged you in general with the irl reference)

woeful pasture
junior holly
#

Why thank you

#

Past what like 2 weeks or something, been doing so much god damn math

#

Really happy with how this looks currently

random yacht
#

Visually looks good :)

#

Sorry, was chatting with family

bronze crag
#

Please, opinion on the idea if it's worth or stupid (the impl is not finished ofc, just wondering if making this makes any sense)

public class CacheWrapper<T> {

    private static final ScheduledExecutorService SCHEDULED_EXECUTOR_SERVICE = Executors.newSingleThreadScheduledExecutor();

    // Some weak key based cache which is going to remove the entry after 5 minutes if the object hasn't been used for all that time
    private static final Map<Object, CacheWrapper<?>> cachedObjectsMap = new HashMap<>();

    private final WeakReference<T> storedObject;

    public CacheWrapper(T object, Consumer<T> cacheAction) {
        this.storedObject = new WeakReference<>(object);

        cacheAction.accept(object);
        SCHEDULED_EXECUTOR_SERVICE.scheduleAtFixedRate(() -> cacheAction.accept(object), 0, 1, TimeUnit.SECONDS);
    }

    public T getStoredObject() {
        return storedObject.get();
    }

    @SuppressWarnings("unchecked")
    public static <T> CacheWrapper<T> of(T object, Consumer<T> cacheAction) {
        return (CacheWrapper<T>) cachedObjectsMap.computeIfAbsent(object, wrapper -> new CacheWrapper<>(object, cacheAction));
    }
}```

Methods which return CompletableFuture aren't not easy to work with, especially when you have too many of them nested
```java
networkInstance.getNetworkPlayerMap().sizeAsync().thenAccept(size -> 
  networkInstance.getNetworkMemoryUsage().thenAccept(memoryUsed -> { 
    sout(size + " " + memoryUsed)
  })
);

Turns into

val playersOnline = CacheWrapper.of(new AtomicInteger(0), value -> value.set(networkInstance.getNetworkPlayerMap().size()));
val memoryUsed = CacheWrapper.of(new AtomicInteger(0), value -> value.set(networkInstance.getMemoryUsage().join()));

sout(playersOnline + " " + memoryUsed)
bronze crag
#

Essentially, this is about objects which can be cached. I was thinking about Hypixel at the moment of writing this.

For example, they have a scoreboard which displays amount of online players on the whole server, let's say that the values on the scoreboard are updated every second and there are 50k players on the server seeing this scoreboard. This would mean, that 50k queries are being processed to the network every second, that's a bit of overkill (I was imagining they use redis and maybe keep players in some RMap).

So, instead of 50k queries/sec to redis, with CacheWrapper we do 1 query/sec instead. I wanted to think of something reusable for other cases and not just one that specific problem, otherwise it could have been done differently

pine grail
#

but why

remote kiln
# pine grail but why

if there is anything heavy what is called frequently, then it can be used with CacheWrapper to decrease load to the servers. Pretty much the example with Hypixel which he's provided

#

I believe that's what he meant

bronze crag
pine grail
#

so just react useState

bronze crag
sharp epoch
#

what is the benefit of this over using something like Caffeine with an expiry after access eviction rule

#

then again, I'm not sure whether there's worth in caching the cache

#

well, I retract that, since when dealing with distributed caches, there's some worth in having a local one. I just don't usually go for that approach since it makes things unnecessarily complex

woeful pasture
#

Cache<Cache<

sharp epoch
#

I mean, if you are using Redis, it gives you all the leeway to go for more shards if necessary

bronze crag
sharp epoch
#

but CacheWrapper would effectively be a single method to abstract out the getting from the cache

#

I don't find that particularly useful, however there might be more of the picture I'm not seeing here

night knoll
sharp epoch
# sharp epoch I don't find that particularly useful, however there might be more of the pictur...

If you are willing to go for a change of focus: in general, if I want to do a non-trivial multi-tiered cache I'd use something like Spring's CA however using it outside of the Spring ecosystem is not really intuitive, so if I have a Redis cache layer then I'd end up using LocalCachedMap, or if I use other distributed cache options (i.e., hazelcast I believe provides a similar abstraction to what you're proposing now). Maybe a way to orchestrate all of that outside of Spring would propose more value, as nobody likes dealing with multi-tiered cache and making that more easier would definitely be a great scope

#

that said, it is infinitely harder to get that right, so be it as you will

clever crag
#

like my plugin

bronze crag
junior holly
rancid fern
# junior holly https://github.com/NormalManV2/NormalDiscGolf Any advice for any area appreciat...
  • Missing Readme
  • Package name should be lowercase
  • NDGApi has no real reason to exist? Everything is already present and accessible in the NormalDiscGolf class.
  • Separate api from impl and place it in it's own module ^^
  • Put test in it's own module (if you plan to keep those commands instead of using unit tests, otherwise move to src/test/java and use something like MockBukkit)
  • Optional: Make a builder for your discs, record + record builder recommended :)
  • The I prefix in the ITeam class is reduntant
  • ITeam interface is not in api package
  • DiscThrowEvent and GoalScoreEvent should be part of api
#

For project structure I recommend looking at LuckPerms

junior holly
rancid fern
#

Didn't look too much at the code itself

junior holly
#

Ah nw

alpine anvil
#

wheres @woeful pasture when we need him

round fossil
#

unless u're working with a really pedantic and complex system, keep it simple and minimalistic when u utilize static state

junior holly
woeful pasture
# junior holly https://github.com/NormalManV2/NormalDiscGolf Any advice for any area appreciat...

Pointless Abstraction https://github.com/NormalManV2/NormalDiscGolf/blob/master/src/main/java/normalmanv2/normalDiscGolf/impl/team/ITeam.java
Another Pointless Abstraction
https://github.com/NormalManV2/NormalDiscGolf/blob/master/src/main/java/normalmanv2/normalDiscGolf/impl/skill/ISkill.java
Is there even a point in skills sharing a common parent. It seems like literally every single skill is just inherited for some reason, but no actual extension is added. Is their even a benefit in a shared ancestor here?? I feel like a lot of these abstractions are just for the sake of "because OOP" versus actual intelligent abstractions. This entire skill thing could be done with a Map<String, Skill> If you ever wanted to add some form of modifiers to the skills you can literally just compose functions within the skills.

I feel like the whole "ThrowTechnique" Stuff could technically be implemented better but honestly its good enough as is

woeful pasture
sly jackal
#

as soon as you start with "I" for your interface you know you have too much abstraction

rancid fern
#

or you are using C# 🔫

sly jackal
#

c# is not an excuse to have bad naming conventions

rancid fern
#

Having I for interfaces is part of the C# convention

round fossil
#

^

rancid fern
#

All of their interfaces have it

round fossil
#

in java we have extends and implements to tell them apart

sly jackal
#

than C# is a bad excuse

round fossil
#

one could say :>

sly jackal
#

but in any case my point was more paired with teh fact that in java if you use I it means you probably have a class that implements it without the I, as in these cases. Which makes it kinda awkward to have any other implementation

#

since then the naming is weird by default

#

like if you have a ExtraSkill, why would it implement ISkill if it can just extend Skill

round fossil
#

well I mean its been proven often that having an over-abstracted system is preferred to an under-abstracted system, especially long term and when you start writing internal library modules and components

sly jackal
#

in fact thats what all the skills are doing anyways

#

yes, but I would pick your names better

round fossil
#

100%

junior holly
#

Well I moved all the interfaces to the api file anyway, I don’t understand why it’s considered a useless abstraction in terms of the skill objects, I feel like I was being lazy and didn’t want to actually write the getter/setters for each

#

Renamed the interfaces as well (without the I)

#

But if I could have a better explanation of why the abstraction is considered useless?

round fossil
#

tbh

#

i didnt read into ur infrastructure

#

but writing apis is hard

#

cuz every class can be considered an api to its consumers/users

every module may have a set of classes that are interfacing the module's functionality to the rest of the system etc

#

there's the literal interface in java, but api can mean a lot less, and a lot more

#

for example luckperms

#

they have an api module, but thats for other plugin devs

#

then they have their own module apis, that is internal abstractions to make the development within the project easier

junior holly
#

So do a lot of people design in a way that’s like “internal api” versus an external if you will

round fossil
#

esp when u have a lot of contributors and collaborators and you have to meet certain standards etc

round fossil
#

on a package level, module level, system level, project level etc

junior holly
#

Mmm I see

round fossil
#

this is also something programming languages usually dont provide any semantics to us

#

meaning there's for example no built in annotation to communicate if a package, or class is an internal or external api and to what audience said api appeals to

#

@ApiStatus.Internal iirc exists in jetbrains annots library

#

but thats 3rd party

#

however, prefixing an interface with I is goofy

#

esp in Java

dense leaf
#

(just public to the JVM)

round fossil
#

ah w for kotlin

junior holly
#

It wasn’t meant to be permanent naming, just for the sake of the ide not yelling at me

round fossil
#

yea

#

push ur updates

#

and ill read through ur code more thoroughly

junior holly
#

All the interfaces were moved to the api package, though I feel GameRound could have remained where it was... not too sure on the semantics of api design obviously so

round fossil
#

alr so ur api sucks

junior holly
#

Yeha

round fossil
#

what is it for?

#

is it for other plugin devs?

junior holly
#

Honestly could not give you a reason... I just moved all the references there because I felt like main was getting bloated kek

round fossil
#

well thats the primary question id ask

#

whats the point of this api package?

junior holly
#

Eventually the goal was to make things accessible to other devs yes

round fossil
#

okay well, u probably completely wanna hide internals then

#

that is anything in the impl package

#

from 3rd party devs

junior holly
#

So in other words, the api I have is useless

sly jackal
#

i guess you mean api for programmers that wanna hook into your plugin from theirs?

junior holly
#

More so from an "extension" standpoint but yeah

sly jackal
#

or is this api intended to just extend your plugin directly

junior holly
#

That was the idea at first, but now that I'm hearing all this, it seems that's not a very uh supported way I guess lol

#

Like I said eventually the goal was to allow 3rd parties to make their own extensions/adjustments kinda thing

round fossil
#

the point of having an api module/package for other devs is that u can change the implementation of ur plugin without forcing plugins that depend on ur plugin to change

#

primarily

#

there are some other nieche things about it also

#

but else, its not so common to centralize interfaces in one package

#

usually u put classes in package by functionality or something like purpose

junior holly
#

I was thinking the only 2 interfaces that really make sense to stay in the api is skill and team, but then again the children are internal impl obviously, so does that even make sense?

round fossil
#

barely

junior holly
#

I guess I just don't really understand what should be considered internal/external

sly jackal
#

the api should probably be created separately from the rest of the code

#

and not also be the core of the implementation

round fossil
#

you should be able to compile the api code without having the rest of ur code on the classpath

junior holly
#

So then again, my api is useless right now kek

round fossil
#

little bit

#

it just looks like u were doing something for the sake of it

#

with no real goal in mind

junior holly
#

Don't sugarcoat it, I can take it man... I'm here to learn lol

junior holly
#

My idea of an api was to provide the accessibility to whatever I needed so perhaps I just don't understand what should be put where

#
public class Team implements normalmanv2.normalDiscGolf.api.Team {

    private final UUID ownerId;
    private final Set<UUID> teamMembers;

    public Team(UUID ownerId) {
        this.ownerId = ownerId;
        this.teamMembers = new HashSet<>();
    }

    @Override
    public void addPlayer(UUID player) {
        this.teamMembers.add(player);
    }

    @Override
    public void removePlayer(UUID player) {
        this.teamMembers.remove(player);
    }

    @Override
    public Set<UUID> getPlayers() {
        return Collections.unmodifiableSet(this.teamMembers);
    }

    @Override
    public UUID getOwner() {
        return this.ownerId;
    }
}```

So when it comes to the "pointless abstraction" bit that miles mentioned, the team object is meant to be used as a disposable object if you will. As for the skills like I already mentioned, I was being lazy and didn't want to write out those bits in each skill
dense leaf
#

why not import Team

junior holly
#

I could see how the team object doesn't need to implement a parent considering the usage goal... still though am a bit confused on the skills aspect

junior holly
round fossil
#

it just depends on what ur appeal of the situation is

#

I generally abstract over almost anything because I often write unit tests, and Ive found that having interfaces can be really nieche long term for maintainability and reusability

#

^in the domain of object orientation

sharp epoch
#

I generally try to avoid abstracting anything when starting out a project

#

just prototype till I get the idea right, then start abstracting

#

it doesn't have to look pretty from the get-go, it just has to work

round fossil
#

very true

#

hard to predict what the ideal structure is gonna look like before u write it

sharp epoch
#

then again, everyone has their methods and it doesn't apply for every situation, but it usually helps harnessing the scope of the project, whether it'll be a time sink or not basically lol

junior holly
#
public class Skill implements normalmanv2.normalDiscGolf.api.Skill {

    private int level;
    private int maxLevel;

    public Skill(int level, int maxLevel) {
        this.level = level;
        this.maxLevel = maxLevel;
    }

    @Override
    public int getLevel() {
        return this.level;
    }

    @Override
    public int getMaxLevel() {
        return this.maxLevel;
    }

    @Override
    public void setLevel(int level) {
        this.level = level;
    }

    @Override
    public void setMaxLevel(int maxLevel) {
        this.maxLevel = maxLevel;
    }
}

...


public class Backhand extends Skill {
    public Backhand(int level, int maxLevel) {
        super(level, maxLevel);
    }
}```

My thought in terms of the skills was to enforce the structure by parent... though internally I only use the skill object to determine what skill and what the current level is, perhaps someone else wants to make a skill differing from my impl
#

Jeez man writing clean code is hard kek

sharp epoch
#

man use actual names that don't conflict with each other 😭

dense leaf
#

SkillImpl

#

or sth

sharp epoch
#

I know thinking of names is hard but just adding Impl or I at the start should do lol

junior holly
#

but fine

sharp epoch
#

BETTER THAN IMPLEMENT my.long.ass.fully.qualified.name.bc.im.lazy.Skill

junior holly
#

ok

#

you're not wrong

woeful pasture
#

I'm still confused what the rational for having a skill interface is

#

same with Team

#

if you just abstracted it because why not that's probably not a great reason. There is such a thing as too much abstraction. Its not like the interface is good API either

#

so I'm confused

junior holly
#

I think laziness is the biggest reason

#

I just thought it'd be best to enforce the structure, but even then I can't find a reason to keep it how it is

sly jackal
#

then dont

junior holly
#

Actually, I feel it just makes me try to think about how the structure of the object should go so to be fair, I think you could consider those interfaces as placeholders if you will. I guess I just like to think of what an object should look like and designing an interface for said object just makes that easier for me

junior holly
sly jackal
#

there are also other options like making silly diagrams that arent part of the code

junior holly
#

Yeah it'd probably be a good idea to take to pen and paper before writing the code

#

Idk it seems like I love to write something out (whether its shite or not) get it reviewed and bum off others experience to make it better kek

#

Should I think about interfaces like this? An interface provides a concrete class structure in which the implementations may differ method to method
And a superclass like: A superclass provides default implementations and variables for it's subclasses

jagged rock
#

An interface is a sort of "contract" that defines a structure of its implementing class. One of its main purposes is to create a strict boundary between "how it works" and "how you use it"

clever crag
#

Check out dev branch

night knoll
junior holly
#

Same lol

rancid fern
#

Yeah he likes to do that

woeful pasture
#

this my friends is why you have your DMs off

#

knowing this though I will probably not be incentivized to actually review this lol

junior holly
#

I mean

#

how was I supposed to know he was trying to like advertise

woeful pasture
#

hold up the real play here is for him to use Pineapple

#

the ultimate spigot library soon to be paper exclusive

junior holly
#

kek

night knoll
night knoll
junior holly
#

Indeed he did

night knoll
#

Why do ppl use 362 APIs?

#

Oh, NBTAPI...

#

I mean just use NMS

#

You're not gonna need all NBTAPI methods

rancid fern
#

Saves time

night knoll
#

Yeah but uses a lot of reflection

#

on methods that you are probably not gonna use

#

Idk, that's my opinion

woeful pasture
#

Reflection is pretty well optimized now

rancid fern
#

True but if you're modifying so much NBT that the performance matters you're doing smth wrong

rancid fern
woeful pasture
#

I mean to people just forget about how its backed by Method Handles which can, in some cases, achieve native call performance?

night knoll
#

Yeah, i know, but why do you need NBTAPI?

woeful pasture
#

or if you need to modify some non exposed field

night knoll
#

You are gonna learn a lot more if you do it by yourself

rancid fern
night knoll
#

PDC saved me tbh

woeful pasture
#

praise lynx 🙏

night knoll
#

Also, i started reading other people code and i think my plugins is absolute GARBAGE right now

#

That's why im recoding it

woeful pasture
junior holly
#

I personally like to spend the time figuring out how to do something myself, not only then do I better understand the underlying concepts but also I feel it helps me connect other dots perhaps when it comes to another system

#

It's also hard to say that what someone else has made will work for my specific goal

night knoll