spaces to tabs (sorry guys)

This commit is contained in:
LAX1DUDE 2022-07-24 12:38:53 -07:00
parent c4bda6bec5
commit d403cb8086
4 changed files with 405 additions and 389 deletions

View File

@ -6,29 +6,30 @@ import net.md_5.bungee.api.plugin.Command;
import net.md_5.bungee.eaglercraft.AuthSystem; import net.md_5.bungee.eaglercraft.AuthSystem;
public class CommandChangePassword extends Command { public class CommandChangePassword extends Command {
private final AuthSystem authSystem; private final AuthSystem authSystem;
public CommandChangePassword(AuthSystem authSystem) { public CommandChangePassword(AuthSystem authSystem) {
super("changepassword", "bungeecord.command.eag.changepassword", new String[] { "changepwd", "changepasswd", "changepass" }); super("changepassword", "bungeecord.command.eag.changepassword",
this.authSystem = authSystem; new String[] { "changepwd", "changepasswd", "changepass" });
} this.authSystem = authSystem;
}
@Override @Override
public void execute(final CommandSender sender, final String[] args) { public void execute(final CommandSender sender, final String[] args) {
if (!(sender instanceof ProxiedPlayer)) { if (!(sender instanceof ProxiedPlayer)) {
return; return;
} }
String username = sender.getName(); String username = sender.getName();
if (args.length == 0 || args.length == 1) { if (args.length == 0 || args.length == 1) {
sender.sendMessage("\u00A7cUsage: /changepassword <oldPassword> <newPassword>"); sender.sendMessage("\u00A7cUsage: /changepassword <oldPassword> <newPassword>");
} else if (this.authSystem.login(username, args[0])) { } else if (this.authSystem.login(username, args[0])) {
if (this.authSystem.changePass(username, args[1])) { if (this.authSystem.changePass(username, args[1])) {
sender.sendMessage("\u00A7cPassword changed successfully!"); sender.sendMessage("\u00A7cPassword changed successfully!");
} else { } else {
sender.sendMessage("\u00A7cUnable to change your password..."); sender.sendMessage("\u00A7cUnable to change your password...");
} }
} else { } else {
sender.sendMessage("\u00A7cThe old password specified is incorrect!"); sender.sendMessage("\u00A7cThe old password specified is incorrect!");
} }
} }
} }

View File

@ -19,135 +19,140 @@ import net.md_5.bungee.protocol.packet.Packet0KeepAlive;
import net.md_5.bungee.protocol.packet.PacketCCSettings; import net.md_5.bungee.protocol.packet.PacketCCSettings;
public class AuthHandler extends PacketHandler { public class AuthHandler extends PacketHandler {
private static final AuthSystem authSystem = BungeeCord.getInstance().authSystem; private static final AuthSystem authSystem = BungeeCord.getInstance().authSystem;
private final ProxyServer bungee; private final ProxyServer bungee;
private final UserConnection con; private final UserConnection con;
private final HandlerBoss handlerBoss; private final HandlerBoss handlerBoss;
private final String username; private final String username;
private boolean loggedIn = false; private boolean loggedIn = false;
public AuthHandler(final ProxyServer bungee, final UserConnection con, final HandlerBoss handlerBoss) { public AuthHandler(final ProxyServer bungee, final UserConnection con, final HandlerBoss handlerBoss) {
this.bungee = bungee; this.bungee = bungee;
this.con = con; this.con = con;
this.handlerBoss = handlerBoss; this.handlerBoss = handlerBoss;
this.username = this.con.getName(); this.username = this.con.getName();
new Thread(() -> { new Thread(() -> {
for (int i = 0; i < 120; i++) { for (int i = 0; i < 120; i++) {
if (this.loggedIn) break; if (this.loggedIn)
try { break;
Thread.sleep(250); try {
} catch (InterruptedException ignored) { } Thread.sleep(250);
} } catch (InterruptedException ignored) {
if (this.loggedIn) return; }
this.con.disconnect("You did not login in time!"); }
}).start(); if (this.loggedIn)
return;
this.con.disconnect("You did not login in time!");
}).start();
this.con.unsafe().sendPacket(new Packet1Login(0, "END", (byte) 2, 1, (byte) 0, (byte) 0, (byte) this.con.getPendingConnection().getListener().getTabListSize())); this.con.unsafe().sendPacket(new Packet1Login(0, "END", (byte) 2, 1, (byte) 0, (byte) 0,
this.con.unsafe().sendPacket(new Packet9Respawn(1, (byte) 0, (byte) 2, (short) 255, "END")); (byte) this.con.getPendingConnection().getListener().getTabListSize()));
this.con.unsafe().sendPacket(new Packet0DPositionAndLook(0, 0, 0, 0, 0f, 0f, true)); this.con.unsafe().sendPacket(new Packet9Respawn(1, (byte) 0, (byte) 2, (short) 255, "END"));
this.con.unsafe().sendPacket(new Packet0DPositionAndLook(0, 0, 0, 0, 0f, 0f, true));
this.con.sendMessages(authSystem.joinMessages); this.con.sendMessages(authSystem.joinMessages);
if (authSystem.isRegistered(this.username)) { if (authSystem.isRegistered(this.username)) {
this.con.sendMessage("\u00A7cPlease login to continue! /login <password>"); this.con.sendMessage("\u00A7cPlease login to continue! /login <password>");
} else { } else {
this.con.sendMessage("\u00A7cPlease register to continue! /register <password> <confirmPassword>"); this.con.sendMessage("\u00A7cPlease register to continue! /register <password> <confirmPassword>");
} }
} }
@Override @Override
public void exception(final Throwable t) throws Exception { public void exception(final Throwable t) throws Exception {
this.con.disconnect(Util.exception(t)); this.con.disconnect(Util.exception(t));
} }
@Override @Override
public void disconnected(final ChannelWrapper channel) { public void disconnected(final ChannelWrapper channel) {
this.loggedIn = true; this.loggedIn = true;
} }
@Override @Override
public void handle(final Packet0KeepAlive alive) throws Exception { public void handle(final Packet0KeepAlive alive) throws Exception {
if (alive.getRandomId() == this.con.getSentPingId()) { if (alive.getRandomId() == this.con.getSentPingId()) {
final int newPing = (int) (System.currentTimeMillis() - this.con.getSentPingTime()); final int newPing = (int) (System.currentTimeMillis() - this.con.getSentPingTime());
this.con.setPing(newPing); this.con.setPing(newPing);
} }
} }
@Override @Override
public void handle(final Packet3Chat chat) throws Exception { public void handle(final Packet3Chat chat) throws Exception {
String message = chat.getMessage(); String message = chat.getMessage();
if (message.startsWith("/")) { if (message.startsWith("/")) {
String[] args = message.substring(1).trim().split(" "); String[] args = message.substring(1).trim().split(" ");
switch (args[0]) { switch (args[0]) {
case "login": case "login":
case "l": case "l":
if (args.length == 1) { if (args.length == 1) {
this.con.sendMessage("\u00A7cYou must specify a password to login! /login <password>"); this.con.sendMessage("\u00A7cYou must specify a password to login! /login <password>");
} else if (!authSystem.isRegistered(this.username)) { } else if (!authSystem.isRegistered(this.username)) {
this.con.sendMessage("\u00A7cThis username is not registered on this server!"); this.con.sendMessage("\u00A7cThis username is not registered on this server!");
} else if (authSystem.login(this.username, args[1])) { } else if (authSystem.login(this.username, args[1])) {
this.con.sendMessage("\u00A7cLogging in..."); this.con.sendMessage("\u00A7cLogging in...");
this.onLogin(); this.onLogin();
} else { } else {
this.con.sendMessage("\u00A7cThat password is invalid!"); this.con.sendMessage("\u00A7cThat password is invalid!");
} }
break; break;
case "register": case "register":
case "reg": case "reg":
if (args.length == 1 || args.length == 2) { if (args.length == 1 || args.length == 2) {
this.con.sendMessage("\u00A7cUsage: /" + args[0].toLowerCase() + " <password> <confirmPassword>"); this.con.sendMessage("\u00A7cUsage: /" + args[0].toLowerCase() + " <password> <confirmPassword>");
} else if (!args[1].equals(args[2])) { } else if (!args[1].equals(args[2])) {
this.con.sendMessage("\u00A7cThose passwords do not match!"); this.con.sendMessage("\u00A7cThose passwords do not match!");
} else if (authSystem.isRegistered(this.username)) { } else if (authSystem.isRegistered(this.username)) {
this.con.sendMessage("\u00A7cThis username is already registered!"); this.con.sendMessage("\u00A7cThis username is already registered!");
} else if (authSystem.register(this.username, args[1], this.con.getAddress().getAddress().getHostAddress())) { } else if (authSystem.register(this.username, args[1],
this.con.sendMessage("\u00A7cSuccessfully registered and logging in..."); this.con.getAddress().getAddress().getHostAddress())) {
this.onLogin(); this.con.sendMessage("\u00A7cSuccessfully registered and logging in...");
} else { this.onLogin();
this.con.sendMessage("\u00A7cUnable to register..."); } else {
} this.con.sendMessage("\u00A7cUnable to register...");
break; }
case "changepassword": break;
case "changepasswd": case "changepassword":
case "changepwd": case "changepasswd":
case "changepass": case "changepwd":
if (args.length == 1 || args.length == 2) { case "changepass":
this.con.sendMessage("\u00A7cUsage: /" + args[0].toLowerCase() + " <oldPassword> <newPassword>"); if (args.length == 1 || args.length == 2) {
} else if (authSystem.login(this.username, args[1])) { this.con.sendMessage("\u00A7cUsage: /" + args[0].toLowerCase() + " <oldPassword> <newPassword>");
if (authSystem.changePass(this.username, args[2])) { } else if (authSystem.login(this.username, args[1])) {
this.con.sendMessage("\u00A7cPassword changed successfully!"); if (authSystem.changePass(this.username, args[2])) {
} else { this.con.sendMessage("\u00A7cPassword changed successfully!");
this.con.sendMessage("\u00A7cUnable to change your password..."); } else {
} this.con.sendMessage("\u00A7cUnable to change your password...");
} else { }
this.con.sendMessage("\u00A7cThe old password specified is incorrect!"); } else {
} this.con.sendMessage("\u00A7cThe old password specified is incorrect!");
break; }
default: break;
} default:
} }
} }
}
private void onLogin() { private void onLogin() {
this.loggedIn = true; this.loggedIn = true;
this.bungee.getPluginManager().callEvent(new PostLoginEvent(this.con)); this.bungee.getPluginManager().callEvent(new PostLoginEvent(this.con));
handlerBoss.setHandler(new UpstreamBridge(this.bungee, this.con)); handlerBoss.setHandler(new UpstreamBridge(this.bungee, this.con));
final ServerInfo server = this.bungee.getReconnectHandler().getServer(this.con); final ServerInfo server = this.bungee.getReconnectHandler().getServer(this.con);
this.con.setServer(new ServerConnection(null, null)); this.con.setServer(new ServerConnection(null, null));
this.con.connect(server, true); this.con.connect(server, true);
} }
@Override @Override
public void handle(final PacketCCSettings settings) throws Exception { public void handle(final PacketCCSettings settings) throws Exception {
this.con.setSettings(settings); this.con.setSettings(settings);
} }
@Override @Override
public String toString() { public String toString() {
return "[" + this.con.getName() + "] -> AuthHandler"; return "[" + this.con.getName() + "] -> AuthHandler";
} }
} }

View File

@ -17,177 +17,186 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public class AuthSystem { public class AuthSystem {
private final String authFileName; private final String authFileName;
private final int ipLimit; private final int ipLimit;
public final String[] joinMessages; public final String[] joinMessages;
public AuthSystem(AuthServiceInfo authInfo) { public AuthSystem(AuthServiceInfo authInfo) {
this.authFileName = authInfo.getAuthfile(); this.authFileName = authInfo.getAuthfile();
this.ipLimit = authInfo.getIpLimit(); this.ipLimit = authInfo.getIpLimit();
List<String> listJoinMessages = authInfo.getJoinMessages(); List<String> listJoinMessages = authInfo.getJoinMessages();
String[] arrayJoinMessages = new String[listJoinMessages.size()]; String[] arrayJoinMessages = new String[listJoinMessages.size()];
for (int i = 0; i < listJoinMessages.size(); i++) { for (int i = 0; i < listJoinMessages.size(); i++) {
arrayJoinMessages[i] = ChatColor.translateAlternateColorCodes('&', listJoinMessages.get(i)); arrayJoinMessages[i] = ChatColor.translateAlternateColorCodes('&', listJoinMessages.get(i));
} }
this.joinMessages = arrayJoinMessages; this.joinMessages = arrayJoinMessages;
this.readDatabase(); this.readDatabase();
} }
private static class AuthData { private static class AuthData {
public String salt; public String salt;
public String hash; public String hash;
public String ip; public String ip;
public long timestamp; public long timestamp;
public AuthData(String salt, String hash, String ip, long timestamp) { public AuthData(String salt, String hash, String ip, long timestamp) {
this.salt = salt; this.salt = salt;
this.hash = hash; this.hash = hash;
this.ip = ip; this.ip = ip;
this.timestamp = timestamp; this.timestamp = timestamp;
} }
} }
private final Map<String, AuthData> database = new HashMap<>(); private final Map<String, AuthData> database = new HashMap<>();
public boolean register(String username, String password, String ip) { public boolean register(String username, String password, String ip) {
synchronized (database) { synchronized (database) {
AuthData authData = database.get(username); AuthData authData = database.get(username);
if (authData != null) return false; if (authData != null)
if (isIpAtTheLimit(ip)) return false; return false;
String salt = createSalt(16); if (isIpAtTheLimit(ip))
String hash = getSaltedHash(password, salt); return false;
database.put(username, new AuthData(salt, hash, ip, System.currentTimeMillis())); String salt = createSalt(16);
writeDatabase(); String hash = getSaltedHash(password, salt);
return true; database.put(username, new AuthData(salt, hash, ip, System.currentTimeMillis()));
} writeDatabase();
} return true;
}
}
public boolean isRegistered(String username) { public boolean isRegistered(String username) {
synchronized (database) { synchronized (database) {
return database.containsKey(username); return database.containsKey(username);
} }
} }
public boolean changePass(String username, String password) { public boolean changePass(String username, String password) {
synchronized (database) { synchronized (database) {
AuthData authData = database.get(username); AuthData authData = database.get(username);
authData.salt = createSalt(16); authData.salt = createSalt(16);
authData.hash = getSaltedHash(password, authData.salt); authData.hash = getSaltedHash(password, authData.salt);
writeDatabase(); writeDatabase();
return true; return true;
} }
} }
public boolean login(String username, String password) { public boolean login(String username, String password) {
synchronized (database) { synchronized (database) {
AuthData authData = database.get(username); AuthData authData = database.get(username);
if (authData == null) return false; if (authData == null)
return authData.hash.equals(getSaltedHash(password, authData.salt)); return false;
} return authData.hash.equals(getSaltedHash(password, authData.salt));
} }
}
private boolean isIpAtTheLimit(String ip) { private boolean isIpAtTheLimit(String ip) {
synchronized (database) { synchronized (database) {
if (this.ipLimit <= 0) return false; if (this.ipLimit <= 0)
int num = 0; return false;
for (AuthData authData : database.values()) { int num = 0;
if (authData.ip.equals(ip)) num++; for (AuthData authData : database.values()) {
if (num >= this.ipLimit) { if (authData.ip.equals(ip))
return true; num++;
} if (num >= this.ipLimit) {
} return true;
return false; }
} }
} return false;
}
}
// only use once, on load // only use once, on load
public void readDatabase() { public void readDatabase() {
synchronized (database) { synchronized (database) {
try { try {
File authFile = new File(this.authFileName); File authFile = new File(this.authFileName);
if (!authFile.exists()) authFile.createNewFile(); if (!authFile.exists())
authFile.createNewFile();
database.clear(); database.clear();
String[] lines = new String(Files.readAllBytes(authFile.toPath())).trim().split("\n"); String[] lines = new String(Files.readAllBytes(authFile.toPath())).trim().split("\n");
if (lines.length == 1 && lines[0].isEmpty()) return; if (lines.length == 1 && lines[0].isEmpty())
boolean alreadyLogged = false; return;
for (String line : lines) { boolean alreadyLogged = false;
String[] pieces = line.split(":"); for (String line : lines) {
if (!pieces[1].startsWith("$SHA$")) { String[] pieces = line.split(":");
if (!alreadyLogged) { if (!pieces[1].startsWith("$SHA$")) {
alreadyLogged = true; if (!alreadyLogged) {
BungeeCord.getInstance().getLogger().warning("One or more entries in the auth file are hashed in an unsupported format! (not SHA-256!)"); alreadyLogged = true;
} BungeeCord.getInstance().getLogger().warning(
// continue; "One or more entries in the auth file are hashed in an unsupported format! (not SHA-256!)");
} }
String[] saltHash = pieces[1].substring(pieces[1].substring(1).indexOf('$') + 2).split("\\$"); // continue;
database.put(pieces[0], new AuthData(saltHash[0], saltHash[1], pieces[2], Long.parseLong(pieces[3]))); }
} String[] saltHash = pieces[1].substring(pieces[1].substring(1).indexOf('$') + 2).split("\\$");
database.put(pieces[0],
new AuthData(saltHash[0], saltHash[1], pieces[2], Long.parseLong(pieces[3])));
}
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }
private void writeDatabase() { private void writeDatabase() {
synchronized (database) { synchronized (database) {
StringBuilder out = new StringBuilder(); StringBuilder out = new StringBuilder();
for (String username : database.keySet()) { for (String username : database.keySet()) {
AuthData entry = database.get(username); AuthData entry = database.get(username);
out.append(username); out.append(username);
out.append(":$SHA$"); out.append(":$SHA$");
out.append(entry.salt); out.append(entry.salt);
out.append("$"); out.append("$");
out.append(entry.hash); out.append(entry.hash);
out.append(":"); out.append(":");
out.append(entry.ip); out.append(entry.ip);
out.append(":"); out.append(":");
out.append(entry.timestamp); out.append(entry.timestamp);
out.append("\n"); out.append("\n");
} }
try { try {
Files.write(Paths.get(this.authFileName), out.toString().getBytes()); Files.write(Paths.get(this.authFileName), out.toString().getBytes());
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }
// hashing used is based on hashing from AuthMe // hashing used is based on hashing from AuthMe
private static final SecureRandom rnd = new SecureRandom(); private static final SecureRandom rnd = new SecureRandom();
private static String getSHA256(String message) { private static String getSHA256(String message) {
try { try {
MessageDigest sha256 = MessageDigest.getInstance("SHA-256"); MessageDigest sha256 = MessageDigest.getInstance("SHA-256");
sha256.reset(); sha256.reset();
sha256.update(message.getBytes()); sha256.update(message.getBytes());
byte[] digest = sha256.digest(); byte[] digest = sha256.digest();
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest)); return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest));
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
return ""; return "";
} }
} }
private static String getSaltedHash(String message, String salt) { private static String getSaltedHash(String message, String salt) {
return getSHA256(getSHA256(message) + salt); return getSHA256(getSHA256(message) + salt);
} }
private static String createSalt(int length) { private static String createSalt(int length) {
try { try {
byte[] msg = new byte[40]; byte[] msg = new byte[40];
rnd.nextBytes(msg); rnd.nextBytes(msg);
MessageDigest sha1 = MessageDigest.getInstance("SHA1"); MessageDigest sha1 = MessageDigest.getInstance("SHA1");
sha1.reset(); sha1.reset();
byte[] digest = sha1.digest(msg); byte[] digest = sha1.digest(msg);
return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest)).substring(0, length); return String.format("%0" + (digest.length << 1) + "x", new BigInteger(1, digest)).substring(0, length);
} catch (NoSuchAlgorithmException e) { } catch (NoSuchAlgorithmException e) {
return ""; return "";
} }
} }
} }

View File

@ -3,112 +3,113 @@ package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
public class Packet0DPositionAndLook extends DefinedPacket { public class Packet0DPositionAndLook extends DefinedPacket {
private double x; private double x;
private double y; private double y;
private double stance; private double stance;
private double z; private double z;
private float yaw; private float yaw;
private float pitch; private float pitch;
private boolean onGround; private boolean onGround;
private Packet0DPositionAndLook() { private Packet0DPositionAndLook() {
super(13); super(13);
} }
public Packet0DPositionAndLook(final int x, final int y, final double stance, final double z, final float yaw, final float pitch, final boolean onGround) { public Packet0DPositionAndLook(final int x, final int y, final double stance, final double z, final float yaw,
this(); final float pitch, final boolean onGround) {
this.x = x; this();
this.y = y; this.x = x;
this.stance = stance; this.y = y;
this.z = z; this.stance = stance;
this.yaw = yaw; this.z = z;
this.pitch = pitch; this.yaw = yaw;
this.onGround = onGround; this.pitch = pitch;
} this.onGround = onGround;
}
@Override @Override
public void read(final ByteBuf buf) { public void read(final ByteBuf buf) {
this.x = buf.readDouble(); this.x = buf.readDouble();
this.y = buf.readDouble(); this.y = buf.readDouble();
this.stance = buf.readDouble(); this.stance = buf.readDouble();
this.z = buf.readDouble(); this.z = buf.readDouble();
this.yaw = buf.readFloat(); this.yaw = buf.readFloat();
this.pitch = buf.readFloat(); this.pitch = buf.readFloat();
this.onGround = buf.readBoolean(); this.onGround = buf.readBoolean();
} }
@Override @Override
public void write(final ByteBuf buf) { public void write(final ByteBuf buf) {
buf.writeDouble(this.x); buf.writeDouble(this.x);
buf.writeDouble(this.y); buf.writeDouble(this.y);
buf.writeDouble(this.stance); buf.writeDouble(this.stance);
buf.writeDouble(this.z); buf.writeDouble(this.z);
buf.writeFloat(this.yaw); buf.writeFloat(this.yaw);
buf.writeFloat(this.pitch); buf.writeFloat(this.pitch);
buf.writeBoolean(this.onGround); buf.writeBoolean(this.onGround);
} }
@Override @Override
public void handle(final AbstractPacketHandler handler) throws Exception { public void handle(final AbstractPacketHandler handler) throws Exception {
handler.handle(this); handler.handle(this);
} }
@Override @Override
public String toString() { public String toString() {
return "Packet0DPositionAndLook(tooLazyToFillThisInLOL)"; return "Packet0DPositionAndLook(tooLazyToFillThisInLOL)";
} }
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (o == this) { if (o == this) {
return true; return true;
} }
if (!(o instanceof Packet0DPositionAndLook)) { if (!(o instanceof Packet0DPositionAndLook)) {
return false; return false;
} }
final Packet0DPositionAndLook other = (Packet0DPositionAndLook) o; final Packet0DPositionAndLook other = (Packet0DPositionAndLook) o;
if (!other.canEqual(this)) { if (!other.canEqual(this)) {
return false; return false;
} }
if (this.x != other.x) { if (this.x != other.x) {
return false; return false;
} }
if (this.y != other.y) { if (this.y != other.y) {
return false; return false;
} }
if (this.stance != other.stance) { if (this.stance != other.stance) {
return false; return false;
} }
if (this.z != other.z) { if (this.z != other.z) {
return false; return false;
} }
if (this.yaw != other.yaw) { if (this.yaw != other.yaw) {
return false; return false;
} }
if (this.pitch != other.pitch) { if (this.pitch != other.pitch) {
return false; return false;
} }
if (this.onGround != other.onGround) { if (this.onGround != other.onGround) {
return false; return false;
} }
return false; return false;
} }
@Override @Override
public int hashCode() { public int hashCode() {
final int PRIME = 31; final int PRIME = 31;
int result = 1; int result = 1;
result = result * 31 + Double.hashCode(this.x); result = result * 31 + Double.hashCode(this.x);
result = result * 31 + Double.hashCode(this.y); result = result * 31 + Double.hashCode(this.y);
result = result * 31 + Double.hashCode(this.stance); result = result * 31 + Double.hashCode(this.stance);
result = result * 31 + Double.hashCode(this.z); result = result * 31 + Double.hashCode(this.z);
result = result * 31 + Float.hashCode(this.yaw); result = result * 31 + Float.hashCode(this.yaw);
result = result * 31 + Float.hashCode(this.pitch); result = result * 31 + Float.hashCode(this.pitch);
result = result * 31 + Boolean.hashCode(this.onGround); result = result * 31 + Boolean.hashCode(this.onGround);
return result; return result;
} }
public boolean canEqual(final Object other) { public boolean canEqual(final Object other) {
return other instanceof Packet0DPositionAndLook; return other instanceof Packet0DPositionAndLook;
} }
} }