Compare commits

...

6 Commits

Author SHA1 Message Date
Helvetica Volubi 77c412bfe8 feat: update concurrent in waypoint 2025-12-11 02:05:57 +08:00
Helvetica Volubi c06d875df7 feat: add optimized concurrent table for waypoint management 2025-12-10 15:22:58 +08:00
Helvetica Volubi 5e96851513 fix a bug in entities counter 2025-12-10 02:52:55 +08:00
Helvetica Volubi 91b3ad527c fix waypoint manager
we use thread safe method to load it
2025-12-10 01:49:10 +08:00
dependabot[bot] 959a1c346c [ci skip]build(deps): bump actions/checkout from 3 to 6 (#102)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-08 15:51:58 +08:00
Helvetica Volubi b594382a6a [ci skip] fixup ci 2025-12-07 19:06:04 +08:00
11 changed files with 475 additions and 19 deletions
+5 -4
View File
@@ -106,11 +106,12 @@ jobs:
- name: Create Release - Pre Process
if: ( github.event_name != 'pull_request' && !( env.unexpect == 'true' ) && env.flag_release == 'true' && env.release_exists != 'true' && !(inputs.force-release == '2')) || inputs.force-release == '1'
run: |
if [ ${{ inputs.comments }} == "" ]; then
echo "No comments provided"
echo "flag_comment=true" >> $GITHUB_ENV
if [ "${{ inputs.comments }}" == "" ]; then
echo "No comments provided"
echo "flag_comment=false" >> $GITHUB_ENV
else
echo "flag_comment=false" >> $GITHUB_ENV
echo "Comments provided: ${{ inputs.comments }}"
echo "flag_comment=true" >> $GITHUB_ENV
fi
- name: Create Release - No Comment
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha }} # to check out the actual pull request commit, not the merge commit
fetch-depth: 0 # a full history is required for pull request analysis
+1 -1
View File
@@ -4,7 +4,7 @@ mcVersion=1.21.8
release=2
# 0 for skip release, 1 for pre-release, 2 for release
luminolRef=46252869190560b69e97c474e91aaa07ac480436
luminolRef=7f354acbc49a52b3a6f7fbc634f96357333c8e59
org.gradle.configuration-cache=true
org.gradle.caching=true
@@ -3,6 +3,7 @@ From: Helvetica Volubi <suisuroru@blue-millennium.fun>
Date: Mon, 7 Jul 2025 18:19:00 +0800
Subject: [PATCH] Add config to enable waypoint command & bar
use concurrent method to fix it (now, they are thread safe)
diff --git a/net/minecraft/commands/Commands.java b/net/minecraft/commands/Commands.java
index 1fb359073342a657e9e493403263b56be5b3393f..451a685782da46f5683ea6707289bbaf3193771f 100644
@@ -22,10 +23,19 @@ index 1fb359073342a657e9e493403263b56be5b3393f..451a685782da46f5683ea6707289bbaf
WorldBorderCommand.register(this.dispatcher);
if (JvmProfiler.INSTANCE.isAvailable()) {
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..52fd730998535ea071bfc99b7cc2c254b9b656d7 100644
index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..55cb2d16e22e4d8745da539177b636d188ad0489 100644
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
@@ -22,7 +22,14 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
@@ -16,22 +16,31 @@ import net.minecraft.world.waypoints.WaypointManager;
import net.minecraft.world.waypoints.WaypointTransmitter;
public class ServerWaypointManager implements WaypointManager<WaypointTransmitter> {
- private final Set<WaypointTransmitter> waypoints = new HashSet<>();
- private final Set<ServerPlayer> players = new HashSet<>();
- private final Table<ServerPlayer, WaypointTransmitter, WaypointTransmitter.Connection> connections = HashBasedTable.create();
+ private final Set<WaypointTransmitter> waypoints = new java.util.concurrent.CopyOnWriteArraySet<>(); // Lophine - concurrent
+ private final Set<ServerPlayer> players = new java.util.concurrent.CopyOnWriteArraySet<>(); // Lophine - concurrent
+ private final fun.bm.lophine.utils.concurrent.AbstractConcurrentTable<ServerPlayer, WaypointTransmitter, WaypointTransmitter.Connection> connections = fun.bm.lophine.config.modules.optimizations.WayPointOptimizedTableConfig.optimizedTable ? new fun.bm.lophine.utils.concurrent.OptimizedConcurrentTable<>() : new fun.bm.lophine.utils.concurrent.ConcurrentTable<>(); // Lophine - concurrent
@Override
public void trackWaypoint(WaypointTransmitter waypoint) {
@@ -41,7 +51,30 @@ index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..52fd730998535ea071bfc99b7cc2c254
}
@Override
@@ -50,14 +57,48 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
public void updateWaypoint(WaypointTransmitter waypoint) {
if (this.waypoints.contains(waypoint)) {
- Map<ServerPlayer, WaypointTransmitter.Connection> map = Tables.transpose(this.connections).row(waypoint);
- SetView<ServerPlayer> set = Sets.difference(this.players, map.keySet());
+ java.util.List<Entry<ServerPlayer, WaypointTransmitter.Connection>> map = this.connections.getXZ(waypoint); // Lophine - concurrent
+ Set<ServerPlayer> players1 = new HashSet<>(); // Lophine - concurrent
+ map.forEach((entry) -> players1.add(entry.getKey())); // Lophine - concurrent
+ SetView<ServerPlayer> set = Sets.difference(this.players, players1); // Lophine - concurrent
- for (Entry<ServerPlayer, WaypointTransmitter.Connection> entry : ImmutableSet.copyOf(map.entrySet())) {
+ for (Entry<ServerPlayer, WaypointTransmitter.Connection> entry : map) { // Lophine - concurrent
this.updateConnection(entry.getKey(), waypoint, entry.getValue());
}
@@ -43,26 +52,60 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
@Override
public void untrackWaypoint(WaypointTransmitter waypoint) {
- this.connections.column(waypoint).forEach((serverPlayer, connection) -> connection.disconnect());
- Tables.transpose(this.connections).row(waypoint).clear();
+ this.connections.getXZ(waypoint).forEach((entry) -> entry.getKey().disconnect()); // Lophine - concurrent
+ this.connections.clearXZ(waypoint); // Lophine - concurrent
this.waypoints.remove(waypoint);
}
public void addPlayer(ServerPlayer player) {
// Folia - region threading
@@ -63,10 +96,12 @@ index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..52fd730998535ea071bfc99b7cc2c254
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ Map<WaypointTransmitter, WaypointTransmitter.Connection> map = this.connections.row(player);
+ SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, map.keySet());
+ java.util.List<Entry<WaypointTransmitter, WaypointTransmitter.Connection>> map = this.connections.getYZ(player); // Lophine - concurrent
+ Set<WaypointTransmitter> waypoints1 = new HashSet<>();// Lophine - concurrent
+ map.forEach((entry) -> waypoints1.add(entry.getKey()));// Lophine - concurrent
+ SetView<WaypointTransmitter> set = Sets.difference(this.waypoints, waypoints1); // Lophine - concurrent
+
+ for (Entry<WaypointTransmitter, WaypointTransmitter.Connection> entry : ImmutableSet.copyOf(map.entrySet())) {
+ for (Entry<WaypointTransmitter, WaypointTransmitter.Connection> entry : map) { // Lophine - concurrent
+ this.updateConnection(player, entry.getKey(), entry.getValue());
+ }
+
@@ -80,13 +115,48 @@ index 0f8cacbb8fe55a60e2f0c98bf36c005b29f41a4b..52fd730998535ea071bfc99b7cc2c254
// Folia - region threading
+ // Lophine start - unsafe waypoint bar
+ if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
+ this.connections.row(player).values().removeIf(connection -> {
+ connection.disconnect();
+ return true;
+ });
+ this.connections.getYZ(player).forEach((entry) -> entry.getValue().disconnect()); // Lophine - concurrent
+ this.connections.clearYZ(player); // Lophine - concurrent
+ this.untrackWaypoint((WaypointTransmitter)player);
+ this.players.remove(player);
+ // Lophine end - unsafe waypoint bar
}
public void breakAllConnections() {
- this.connections.values().forEach(WaypointTransmitter.Connection::disconnect);
- this.connections.clear();
+ this.connections.getAllZ().forEach(WaypointTransmitter.Connection::disconnect); // Lophine - concurrent
+ this.connections.clearAll(); // Lophine - concurrent
}
public void remakeConnections(WaypointTransmitter waypoint) {
@@ -86,10 +129,15 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
this.connections.put(player, waypoint, connection);
connection.connect();
}, () -> {
- WaypointTransmitter.Connection connection = this.connections.remove(player, waypoint);
- if (connection != null) {
- connection.disconnect();
- }
+ // Lophine start - concurrent
+ java.util.List<WaypointTransmitter.Connection> c = this.connections.getZ(player, waypoint);
+ c.forEach((connection) -> {
+ this.connections.remove(player, waypoint, connection);
+ if (connection != null) {
+ connection.disconnect();
+ }
+ });
+ // Lophine end - concurrent
});
}
}
@@ -106,7 +154,8 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
this.connections.put(player, waypoint, connection1);
}, () -> {
connection.disconnect();
- this.connections.remove(player, waypoint);
+ java.util.List<WaypointTransmitter.Connection> c = this.connections.getZ(player, waypoint); // Lophine - concurrent
+ c.forEach((connection1) -> this.connections.remove(player, waypoint, connection1)); // Lophine - concurrent
});
}
}
@@ -511,7 +511,7 @@ index 8e2775e873c92beed6a0e4e8ce1304ab8ca6e8d1..580f114fd99b017403c61e994d0c947d
public boolean canBypassPlayerLimit(GameProfile profile) {
diff --git a/net/minecraft/server/waypoints/ServerWaypointManager.java b/net/minecraft/server/waypoints/ServerWaypointManager.java
index 52fd730998535ea071bfc99b7cc2c254b9b656d7..0abf6f0265fcb916f2c2c76fb1313bd13444f4ad 100644
index 55cb2d16e22e4d8745da539177b636d188ad0489..ce0942793c4098a1274b8ef5736e824a6eec0152 100644
--- a/net/minecraft/server/waypoints/ServerWaypointManager.java
+++ b/net/minecraft/server/waypoints/ServerWaypointManager.java
@@ -24,6 +24,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
@@ -526,7 +526,7 @@ index 52fd730998535ea071bfc99b7cc2c254b9b656d7..0abf6f0265fcb916f2c2c76fb1313bd1
this.waypoints.add(waypoint);
for (ServerPlayer serverPlayer : this.players) {
@@ -59,6 +64,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
@@ -61,6 +66,11 @@ public class ServerWaypointManager implements WaypointManager<WaypointTransmitte
// Folia - region threading
// Lophine start - unsafe waypoint bar
if (!fun.bm.lophine.config.modules.experiment.CommandConfig.waypoint) return;
@@ -0,0 +1,17 @@
package fun.bm.lophine.config.modules.optimizations;
import me.earthme.luminol.config.IConfigModule;
import me.earthme.luminol.config.flags.ConfigClassInfo;
import me.earthme.luminol.config.flags.ConfigInfo;
import me.earthme.luminol.config.flags.HotReloadUnsupported;
import me.earthme.luminol.enums.EnumConfigCategory;
@ConfigClassInfo(category = EnumConfigCategory.OPTIMIZATIONS, name = "waypoint")
public class WayPointOptimizedTableConfig implements IConfigModule {
@HotReloadUnsupported
@ConfigInfo(name = "optimizedTable", comments = """
Should use optimized table instead of normal concurrent table for waypoints.
May improve performance when there are many waypoints and players.
When enabled, more memory is needed to store data.""")
public static boolean optimizedTable = false;
}
@@ -55,7 +55,7 @@ public class EntitiesCounterUtil {
synchronized (globalLoadedEntities) {
globalLoadedEntities.get(level).remove(uniqueId);
}
synchronized (mobsMap) {
synchronized (mobsAreaMap) {
mobsAreaMap.get(level).remove(uniqueId);
}
}
@@ -0,0 +1,36 @@
package fun.bm.lophine.utils.concurrent;
import java.util.List;
import java.util.Map;
public abstract class AbstractConcurrentTable<X, Y, Z> {
public abstract void put(X x, Y y, Z z);
public abstract void remove(X x, Y y, Z z);
public abstract List<Z> getZ(X x, Y y);
public abstract List<Y> getY(X x, Z z);
public abstract List<X> getX(Y y, Z z);
public abstract List<Map.Entry<X, Y>> getXY(Z z);
public abstract List<Map.Entry<Y, Z>> getYZ(X x);
public abstract List<Map.Entry<X, Z>> getXZ(Y y);
public abstract List<X> getAllX();
public abstract List<Y> getAllY();
public abstract List<Z> getAllZ();
public abstract void clearXY(Z z);
public abstract void clearYZ(X x);
public abstract void clearXZ(Y y);
public abstract void clearAll();
}
@@ -0,0 +1,139 @@
package fun.bm.lophine.utils.concurrent;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.function.Predicate;
public class ConcurrentTable<X, Y, Z> extends AbstractConcurrentTable<X, Y, Z> {
protected final ConcurrentLinkedDeque<TableEntry<X, Y, Z>> data = new ConcurrentLinkedDeque<>();
@Override
public void put(X x, Y y, Z z) {
data.add(new TableEntry<>(x, y, z));
}
@Override
public void remove(X x, Y y, Z z) {
data.removeIf(entry -> entry.getX().equals(x) && entry.getY().equals(y) && entry.getZ().equals(z));
}
@Override
public List<Z> getZ(X x, Y y) {
return filterAndCollect(
entry -> entry.getX().equals(x) && entry.getY().equals(y),
TableEntry::getZ
);
}
@Override
public List<Y> getY(X x, Z z) {
return filterAndCollect(
entry -> entry.getX().equals(x) && entry.getZ().equals(z),
TableEntry::getY
);
}
@Override
public List<X> getX(Y y, Z z) {
return filterAndCollect(
entry -> entry.getY().equals(y) && entry.getZ().equals(z),
TableEntry::getX
);
}
@Override
public List<Map.Entry<X, Y>> getXY(Z z) {
return filterAndMap(
entry -> entry.getZ().equals(z),
TableEntry::getX,
TableEntry::getY
);
}
@Override
public List<Map.Entry<Y, Z>> getYZ(X x) {
return filterAndMap(
entry -> entry.getX().equals(x),
TableEntry::getY,
TableEntry::getZ
);
}
@Override
public List<Map.Entry<X, Z>> getXZ(Y y) {
return filterAndMap(
entry -> entry.getY().equals(y),
TableEntry::getX,
TableEntry::getZ
);
}
@Override
public List<X> getAllX() {
return collectAll(TableEntry::getX);
}
@Override
public List<Y> getAllY() {
return collectAll(TableEntry::getY);
}
@Override
public List<Z> getAllZ() {
return collectAll(TableEntry::getZ);
}
@Override
public void clearXY(Z z) {
data.removeIf(entry -> entry.getZ().equals(z));
}
@Override
public void clearYZ(X x) {
data.removeIf(entry -> entry.getX().equals(x));
}
@Override
public void clearXZ(Y y) {
data.removeIf(entry -> entry.getY().equals(y));
}
@Override
public void clearAll() {
data.clear();
}
private <T> List<T> filterAndCollect(Predicate<TableEntry<X, Y, Z>> filter,
java.util.function.Function<TableEntry<X, Y, Z>, T> mapper) {
List<T> result = new ArrayList<>();
for (TableEntry<X, Y, Z> entry : data) {
if (filter.test(entry)) {
result.add(mapper.apply(entry));
}
}
return result;
}
private <K, V> List<Map.Entry<K, V>> filterAndMap(Predicate<TableEntry<X, Y, Z>> filter,
java.util.function.Function<TableEntry<X, Y, Z>, K> keyMapper,
java.util.function.Function<TableEntry<X, Y, Z>, V> valueMapper) {
List<Map.Entry<K, V>> list = new ArrayList<>();
for (TableEntry<X, Y, Z> entry : data) {
if (filter.test(entry)) {
list.add(new AbstractMap.SimpleEntry<>(keyMapper.apply(entry), valueMapper.apply(entry)));
}
}
return list;
}
private <T> List<T> collectAll(java.util.function.Function<TableEntry<X, Y, Z>, T> mapper) {
List<T> result = new ArrayList<>();
for (TableEntry<X, Y, Z> entry : data) {
result.add(mapper.apply(entry));
}
return result;
}
}
@@ -0,0 +1,168 @@
package fun.bm.lophine.utils.concurrent;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiFunction;
import java.util.function.Predicate;
public class OptimizedConcurrentTable<X, Y, Z> extends ConcurrentTable<X, Y, Z> {
private final ConcurrentHashMap<X, ConcurrentHashMap<Y, Set<Z>>> xyIndex = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Y, ConcurrentHashMap<Z, Set<X>>> yzIndex = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Z, ConcurrentHashMap<X, Set<Y>>> zxIndex = new ConcurrentHashMap<>();
@Override
public void put(X x, Y y, Z z) {
super.put(x, y, z);
xyIndex.computeIfAbsent(x, k -> new ConcurrentHashMap<>())
.computeIfAbsent(y, k -> ConcurrentHashMap.newKeySet()).add(z);
yzIndex.computeIfAbsent(y, k -> new ConcurrentHashMap<>())
.computeIfAbsent(z, k -> ConcurrentHashMap.newKeySet()).add(x);
zxIndex.computeIfAbsent(z, k -> new ConcurrentHashMap<>())
.computeIfAbsent(x, k -> ConcurrentHashMap.newKeySet()).add(y);
}
@Override
public void remove(X x, Y y, Z z) {
super.remove(x, y, z);
removeFromIndex(xyIndex, x, y, z);
removeFromIndex(yzIndex, y, z, x);
removeFromIndex(zxIndex, z, x, y);
}
private <K, V, T> void removeFromIndex(ConcurrentHashMap<K, ConcurrentHashMap<V, Set<T>>> index,
K key1, V key2, T value) {
index.computeIfPresent(key1, (k, map) -> {
map.computeIfPresent(key2, (k2, set) -> {
set.remove(value);
return set.isEmpty() ? null : set;
});
return map.isEmpty() ? null : map;
});
}
public void removeAll(Predicate<TableEntry<X, Y, Z>> predicate) {
data.removeIf(entry -> {
boolean shouldRemove = predicate.test(entry);
if (shouldRemove) {
removeFromIndex(xyIndex, entry.getX(), entry.getY(), entry.getZ());
removeFromIndex(yzIndex, entry.getY(), entry.getZ(), entry.getX());
removeFromIndex(zxIndex, entry.getZ(), entry.getX(), entry.getY());
}
return shouldRemove;
});
}
public boolean putIfAbsent(X x, Y y, Z z) {
if (data.stream().anyMatch(entry ->
Objects.equals(entry.getX(), x) &&
Objects.equals(entry.getY(), y) &&
Objects.equals(entry.getZ(), z))) {
return false;
}
put(x, y, z);
return true;
}
@Override
public List<Z> getZ(X x, Y y) {
Set<Z> result = xyIndex.getOrDefault(x, new ConcurrentHashMap<>()).get(y);
return result != null ? new ArrayList<>(result) : new ArrayList<>();
}
@Override
public List<Y> getY(X x, Z z) {
Set<Y> result = zxIndex.getOrDefault(z, new ConcurrentHashMap<>()).get(x);
return result != null ? new ArrayList<>(result) : new ArrayList<>();
}
public List<X> getX(Y y, Z z) {
Set<X> result = yzIndex.getOrDefault(y, new ConcurrentHashMap<>()).get(z);
return result != null ? new ArrayList<>(result) : new ArrayList<>();
}
@Override
public List<Map.Entry<X, Y>> getXY(Z z) {
return buildDataFromIndex(zxIndex.get(z));
}
@Override
public List<Map.Entry<Y, Z>> getYZ(X x) {
return buildDataFromIndex(xyIndex.get(x));
}
@Override
public List<Map.Entry<X, Z>> getXZ(Y y) {
return reverseDataFromIndex(yzIndex.get(y));
}
@Override
public List<X> getAllX() {
Set<X> resultSet = new HashSet<>(xyIndex.keySet());
return new ArrayList<>(resultSet);
}
@Override
public List<Y> getAllY() {
Set<Y> resultSet = new HashSet<>(yzIndex.keySet());
return new ArrayList<>(resultSet);
}
@Override
public List<Z> getAllZ() {
Set<Z> resultSet = new HashSet<>(zxIndex.keySet());
return new ArrayList<>(resultSet);
}
@Override
public void clearXY(Z z) {
super.clearXY(z);
zxIndex.remove(z);
}
@Override
public void clearYZ(X x) {
super.clearYZ(x);
xyIndex.remove(x);
}
@Override
public void clearXZ(Y y) {
super.clearXZ(y);
yzIndex.remove(y);
}
@Override
public void clearAll() {
super.clearAll();
xyIndex.clear();
yzIndex.clear();
zxIndex.clear();
}
private <K, V, R> List<R> buildEntriesFromIndex(ConcurrentHashMap<K, Set<V>> indexMap,
BiFunction<K, V, R> entryCreator) {
List<R> result = new ArrayList<>();
if (indexMap != null) {
for (Map.Entry<K, Set<V>> entry : indexMap.entrySet()) {
K key = entry.getKey();
Set<V> valueSet = entry.getValue();
if (valueSet != null && !valueSet.isEmpty()) {
for (V value : valueSet) {
result.add(entryCreator.apply(key, value));
}
}
}
}
return result;
}
private <K, V> List<Map.Entry<K, V>> buildDataFromIndex(ConcurrentHashMap<K, Set<V>> indexMap) {
return buildEntriesFromIndex(indexMap, AbstractMap.SimpleEntry::new);
}
private <K, V> List<Map.Entry<V, K>> reverseDataFromIndex(ConcurrentHashMap<K, Set<V>> indexMap) {
return buildEntriesFromIndex(indexMap, (key, value) -> new AbstractMap.SimpleEntry<>(value, key));
}
}
@@ -0,0 +1,25 @@
package fun.bm.lophine.utils.concurrent;
public class TableEntry<X, Y, Z> {
private final X x;
private final Y y;
private final Z z;
public TableEntry(X x, Y y, Z z) {
this.x = x;
this.y = y;
this.z = z;
}
public X getX() {
return x;
}
public Y getY() {
return y;
}
public Z getZ() {
return z;
}
}