Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 20 additions & 11 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,22 +1,31 @@
root = true

[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = space
indent_size = 4
ij_any_block_comment_add_space = false
ij_any_block_comment_at_first_column = false
ij_any_line_comment_at_first_column = false
ij_any_line_comment_add_space = true

[*.bat]
end_of_line = crlf

[{*.yaml,*.yml}]
[*.yml]
indent_size = 2
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
ij_yaml_indent_sequence_value = true
ij_yaml_keep_indents_on_empty_lines = false
ij_yaml_keep_line_breaks = true
ij_yaml_sequence_on_new_line = false
ij_yaml_space_before_colon = false
ij_yaml_spaces_within_braces = true
ij_yaml_spaces_within_brackets = true

[*.java]
ij_continuation_indent_size = 4
ij_java_class_count_to_use_import_on_demand = 999999
ij_java_insert_inner_class_imports = false
ij_java_names_count_to_use_import_on_demand = 999999
ij_java_imports_layout = *, |, $*
ij_java_generate_final_locals = true
ij_java_generate_final_parameters = true
ij_java_method_parameters_new_line_after_left_paren = true
ij_java_method_parameters_right_paren_on_new_line = true
ij_java_use_fq_class_names = false
ij_java_class_names_in_javadoc = 1
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing to LobbyHeads

:Wave: Hello there!
🖐 Hello there!
We wholeheartedly welcome all contributions to LobbyHeads and are excited to know you are considering making one.

To ensure a robust and ideal solution, we recommend going through this guide.
Expand Down
14 changes: 0 additions & 14 deletions .whitesource

This file was deleted.

2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

Copyright (C) 2023 EternalCodeteam
Copyright (C) 2025 EternalCodeteam
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
Expand Down
20 changes: 9 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

![LobbyHeads](/assets/lobbyheads-banner.png)

[![Supported by Paper](https://raw.githubusercontent.com/intergrav/devins-badges/v3/assets/cozy/supported/paper_vector.svg)](https://papermc.io)
[![Supported by Spigot](https://raw.githubusercontent.com/intergrav/devins-badges/v3/assets/cozy/supported/spigot_vector.svg)](https://spigotmc.org)
[![Available on SpigotMC](https://raw.githubusercontent.com/vLuckyyy/badges/main/available-on-spigotmc.svg)](https://www.spigotmc.org/resources/lobbyheads-%E2%9C%A8-decorative-heads-for-your-server-hub.113065/)
[![Available on Modrinth](https://raw.githubusercontent.com/vLuckyyy/badges/main/avaiable-on-modrinth.svg)](https://modrinth.com/plugin/LobbyHeads)
[![Available on Hangar](https://raw.githubusercontent.com/vLuckyyy/badges/main/avaiable-on-hangar.svg)](https://hangar.papermc.io/EternalCodeTeam/LobbyHeads)

[![Patreon](https://raw.githubusercontent.com/intergrav/devins-badges/v3/assets/cozy/donate/patreon-plural_vector.svg)](https://www.patreon.com/eternalcode)
[![Official Website](https://raw.githubusercontent.com/intergrav/devins-badges/v3/assets/cozy/documentation/website_vector.svg)](https://eternalcode.pl/)
[![Join Our Discord](https://raw.githubusercontent.com/intergrav/devins-badges/v3/assets/cozy/social/discord-plural_vector.svg)](https://discord.gg/FQ7jmGBd6c)

[![Built with Gradle](https://raw.githubusercontent.com/intergrav/devins-badges/v3/assets/cozy/built-with/gradle_vector.svg)](https://gradle.org/)
[![Built with Java](https://raw.githubusercontent.com/intergrav/devins-badges/v3/assets/cozy/built-with/java17_vector.svg)](https://www.java.com/)
[![Chat on Discord](https://raw.githubusercontent.com/vLuckyyy/badges/main//chat-with-us-on-discord.svg)](https://discord.com/invite/FQ7jmGBd6c)
[![Read the Docs](https://raw.githubusercontent.com/vLuckyyy/badges/main/read-the-documentation.svg)](https://docs.eternalcode.pl/eternalcore/introduction)
[![Available on BStats](https://raw.githubusercontent.com/vLuckyyy/badges/main/available-on-bstats.svg)](https://bstats.org/plugin/bukkit/LobbyHeads/20048)
</div>

## About LobbyHeads
Expand All @@ -24,11 +22,11 @@ If you find any bugs, issues or inconsistencies, kindly report them [here](https
## Plugin Preview

### How head work? (You can use this example for **premium** ranks on your server!)
![gif](/assets/head%20work%2020fps.gif)
![gif](https://github.com/EternalCodeTeam/LobbyHeads/blob/master/assets/head%20work%2020fps.gif?raw=true)


### How to set up heads? (`/heads add` comand)
![gif](assets/head%20setup%2020fps.gif)
![gif](https://github.com/EternalCodeTeam/LobbyHeads/blob/master/assets/head%20setup%2020fps.gif?raw=true)

## Permission Assignments

Expand Down Expand Up @@ -59,7 +57,7 @@ The output file `LobbyHeads-<version>.jar` will be located in the `build/libs` d

### Contributions

We wholeheartedly welcome your contributions to LobbyHeads! Please refer to our [contribution guidelines](.github/CONTRIBUTING.md) for detailed procedures on how you can contribute, and our [code of conduct](./.github/CODE_OF_CONDUCT.md) to ensure a harmonious and welcoming community.
We wholeheartedly welcome your contributions to LobbyHeads! Please refer to our [contribution guidelines](https://github.com/EternalCodeTeam/LobbyHeads/blob/master/.github/CONTRIBUTING.md) for detailed procedures on how you can contribute, and our [code of conduct](./.github/CODE_OF_CONDUCT.md) to ensure a harmonious and welcoming community.

### Issue Reporting

Expand Down
8 changes: 0 additions & 8 deletions buildSrc/src/main/kotlin/lobbyheads-java-17.gradle.kts

This file was deleted.

16 changes: 16 additions & 0 deletions buildSrc/src/main/kotlin/lobbyheads-java-21.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
plugins {
id("java-library")
}

group = "com.eternalcode"
version = "1.0.4-SNAPSHOT"

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(21))
}

tasks.compileJava {
options.compilerArgs = listOf("-Xlint:deprecation", "-parameters")
options.encoding = "UTF-8"
options.release = 21
}
19 changes: 0 additions & 19 deletions buildSrc/src/main/kotlin/lobbyheads-java-unit-test.gradle.kts

This file was deleted.

4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/lobbyheads-repositories.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ plugins {
repositories {
mavenCentral()

maven { url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") }
maven { url = uri("https://repo.papermc.io/repository/maven-public/") }
maven { url = uri("https://jitpack.io") }
maven { url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/") }
maven { url = uri("https://repo.eternalcode.pl/releases") }
maven { url = uri("https://storehouse.okaeri.eu/repository/maven-public/") }
maven { url = uri("https://repository.minecodes.pl/releases") }
maven { url = uri("https://libraries.minecraft.net/") }
maven { url = uri("https://repo.fancyplugins.de/releases") }
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Expand Down
4 changes: 2 additions & 2 deletions lobbyheads-api/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
plugins {
`lobbyheads-java-17`
`lobbyheads-java-21`
`lobbyheads-repositories`
`lobbyheads-checkstyle`
}

dependencies {
api("org.spigotmc:spigot-api:1.20.2-R0.1-SNAPSHOT")
compileOnlyApi("io.papermc.paper:paper-api:1.21.7-R0.1-SNAPSHOT")
api("org.jetbrains:annotations:24.1.0")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
package com.eternalcode.lobbyheads;

import com.eternalcode.lobbyheads.head.HeadManager;
import org.jetbrains.annotations.NotNull;

/**
* Root entry point for interacting with the LobbyHeads API.
*/
public interface LobbyHeadsApi {

HeadManager getHeadManager();
@NotNull HeadManager getHeadManager();
}
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
package com.eternalcode.lobbyheads;

public class LobbyHeadsApiProvider {
import org.jetbrains.annotations.NotNull;

private static LobbyHeadsApi lobbyHeadsApi;
/**
* Provides global access to the LobbyHeads API.
*/
public final class LobbyHeadsApiProvider {

public static LobbyHeadsApi provide() {
if (lobbyHeadsApi == null) {
throw new IllegalStateException("LobbyHeadsApi has not been initialized yet!");
}
private static volatile LobbyHeadsApi api;

return lobbyHeadsApi;
private LobbyHeadsApiProvider() {
throw new UnsupportedOperationException("This is a utility class.");
}

static void initialize(LobbyHeadsApi lobbyHeadsApi) {
if (LobbyHeadsApiProvider.lobbyHeadsApi != null) {
throw new IllegalStateException("LobbyHeadsApi has already been initialized!");
/**
* @return The initialized LobbyHeadsApi instance.
* @throws IllegalStateException if not initialized.
*/
public static @NotNull LobbyHeadsApi get() {
LobbyHeadsApi instance = api;
if (instance == null) {
throw new IllegalStateException("LobbyHeadsApi is not initialized yet.");
}
return instance;
}

LobbyHeadsApiProvider.lobbyHeadsApi = lobbyHeadsApi;
/**
* Internal use only.
*/
static void initialize(@NotNull LobbyHeadsApi lobbyHeadsApi) {
if (api != null) {
throw new IllegalStateException("LobbyHeadsApi is already initialized.");
}
api = lobbyHeadsApi;
}

static void deinitialize() {
if (lobbyHeadsApi == null) {
throw new IllegalStateException("LobbyHeadsApi has not been initialized yet!");
if (api == null) {
throw new IllegalStateException("LobbyHeadsApi is not initialized.");
}

lobbyHeadsApi = null;
api = null;
}
}
Original file line number Diff line number Diff line change
@@ -1,40 +1,54 @@
package com.eternalcode.lobbyheads.head;

import com.eternalcode.lobbyheads.position.Position;
import com.eternalcode.lobbyheads.position.PositionAdapter;
import java.io.Serializable;
import java.util.Objects;
import java.util.UUID;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;

public class Head {
public class Head implements Serializable {

private final String world;
private final double x, y, z;
private final float yaw, pitch;

private final Position position;
private String playerName;
private UUID playerUUID;

public Head(Position position, String playerName, UUID playerUUID) {
this.position = position;
public Head() {
this.world = null;
this.x = this.y = this.z = 0;
this.yaw = this.pitch = 0;
}
Comment on lines +18 to +22
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Watch out for null world!

The no-arg constructor sets world to null, which will cause problems when getLocation() is called later.

🤖 Prompt for AI Agents
In lobbyheads-api/src/main/java/com/eternalcode/lobbyheads/head/Head.java around
lines 18 to 22, the no-argument constructor sets the world field to null, which
will cause issues when getLocation() is called. To fix this, initialize the
world field with a valid default World instance or modify the constructor to
require a non-null world parameter, ensuring that world is never null when used
later.


public Head(@NotNull Location location, String playerName, UUID playerUUID) {
this.world = location.getWorld().getName();
this.x = location.getX();
this.y = location.getY();
this.z = location.getZ();
this.yaw = location.getYaw();
this.pitch = location.getPitch();
this.playerName = playerName;
this.playerUUID = playerUUID;
}

public Position getPosition() {
return this.position;
public @NotNull Location getLocation() {
return new Location(
org.bukkit.Bukkit.getWorld(this.world),
this.x, this.y, this.z,
this.yaw, this.pitch
);
}
Comment on lines +35 to 41
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add null safety for world

The getLocation() method could throw NPE if world is null or if Bukkit can't find the world.

Consider adding a null check:

 public @NotNull Location getLocation() {
+    if (this.world == null) {
+        throw new IllegalStateException("World is not set");
+    }
+    var world = org.bukkit.Bukkit.getWorld(this.world);
+    if (world == null) {
+        throw new IllegalStateException("World '" + this.world + "' not found");
+    }
     return new Location(
-        org.bukkit.Bukkit.getWorld(this.world),
+        world,
         this.x, this.y, this.z,
         this.yaw, this.pitch
     );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public @NotNull Location getLocation() {
return new Location(
org.bukkit.Bukkit.getWorld(this.world),
this.x, this.y, this.z,
this.yaw, this.pitch
);
}
public @NotNull Location getLocation() {
if (this.world == null) {
throw new IllegalStateException("World is not set");
}
var world = org.bukkit.Bukkit.getWorld(this.world);
if (world == null) {
throw new IllegalStateException("World '" + this.world + "' not found");
}
return new Location(
world,
this.x, this.y, this.z,
this.yaw, this.pitch
);
}
🤖 Prompt for AI Agents
In lobbyheads-api/src/main/java/com/eternalcode/lobbyheads/head/Head.java around
lines 35 to 41, the getLocation() method may throw a NullPointerException if the
world field is null or if Bukkit.getWorld(this.world) returns null. Add a null
check for the world object returned by Bukkit.getWorld(this.world) before
creating the Location instance. If the world is null, handle it gracefully by
either returning null, throwing a custom exception, or using an Optional to
indicate absence, depending on the intended design.


public Location getLocation() {
return PositionAdapter.convert(this.position);
public @NotNull UUID getPlayerUuid() {
return this.playerUUID;
}

public String getPlayerName() {
public @NotNull String getPlayerName() {
return this.playerName;
}

public UUID getPlayerUUID() {
return this.playerUUID;
}

public void replacePlayer(String newPlayerName, UUID newPlayerUUID) {
public void replacePlayer(@NotNull String newPlayerName, @NotNull UUID newPlayerUUID) {
this.playerName = newPlayerName;
this.playerUUID = newPlayerUUID;
}
Expand All @@ -44,17 +58,15 @@ public boolean equals(Object object) {
if (this == object) {
return true;
}

if (object == null || getClass() != object.getClass()) {
if (!(object instanceof Head other)) {
return false;
}

Head head = (Head) object;
return Objects.equals(this.position, head.position);
return this.world.equals(other.world) &&
this.x == other.x && this.y == other.y && this.z == other.z;
}
Comment on lines +64 to 66
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Missing null check in equals

The equals method doesn't check if world is null before calling .equals().

Add a null check:

-return this.world.equals(other.world) &&
+return Objects.equals(this.world, other.world) &&
     this.x == other.x && this.y == other.y && this.z == other.z;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return this.world.equals(other.world) &&
this.x == other.x && this.y == other.y && this.z == other.z;
}
return Objects.equals(this.world, other.world) &&
this.x == other.x && this.y == other.y && this.z == other.z;
}
🤖 Prompt for AI Agents
In lobbyheads-api/src/main/java/com/eternalcode/lobbyheads/head/Head.java around
lines 64 to 66, the equals method calls this.world.equals(other.world) without
checking if this.world is null, which can cause a NullPointerException. Fix this
by adding a null check for this.world before calling equals, for example by
using Objects.equals(this.world, other.world) or explicitly checking if
this.world is null and comparing accordingly.


@Override
public int hashCode() {
return Objects.hash(this.position);
return Objects.hash(this.world, this.x, this.y, this.z);
}
}
Loading
Loading