This commit is contained in:
Steveice10 2014-01-24 16:03:58 -08:00
parent 416f8b0ebe
commit b4d03d2308
42 changed files with 547 additions and 204 deletions

View file

@ -20,7 +20,7 @@ See ch.spacebase.mc.protocol.test.Test.
--------
MCProtocolLib uses Maven to manage dependencies. Simply run 'mvn clean install' in the source's directory.
You can also download a build <b>[here](http://build.spacebase.ch/job/MCProtocolLib14w03b/)</b>.
You can also download a build <b>[here](http://build.spacebase.ch/job/MCProtocolLib14w04b/)</b>.
<b>License</b>

View file

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>ch.spacebase</groupId>
<artifactId>mcprotocollib</artifactId>
<version>14w03b-SNAPSHOT</version>
<version>14w04b-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MCProtocolLib</name>
@ -85,6 +85,13 @@
<build>
<defaultGoal>clean install</defaultGoal>
<sourceDirectory>${basedir}/src/main/java</sourceDirectory>
<!-- Resources -->
<resources>
<resource>
<directory>${basedir}/src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

View file

@ -1,9 +1,14 @@
package ch.spacebase.mc.auth;
import java.util.HashMap;
import java.util.Map;
public class GameProfile {
private String id;
private String name;
private Map<String, ProfileProperty> properties = new HashMap<String, ProfileProperty>();
private boolean legacy;
public GameProfile(String id, String name) {
if((id == null || id.equals("")) && (name == null || name.equals(""))) {
@ -22,6 +27,14 @@ public class GameProfile {
return this.name;
}
public Map<String, ProfileProperty> getProperties() {
return this.properties;
}
public boolean isLegacy() {
return this.legacy;
}
public boolean isComplete() {
return this.id != null && !this.id.equals("") && this.name != null && !this.name.equals("");
}

View file

@ -0,0 +1,54 @@
package ch.spacebase.mc.auth;
import java.security.PublicKey;
import java.security.Signature;
import ch.spacebase.mc.util.Base64;
public class ProfileProperty {
private String name;
private String value;
private String signature;
public ProfileProperty(String value, String name) {
this(value, name, null);
}
public ProfileProperty(String name, String value, String signature) {
this.name = name;
this.value = value;
this.signature = signature;
}
public String getName() {
return this.name;
}
public String getValue() {
return this.value;
}
public String getSignature() {
return this.signature;
}
public boolean hasSignature() {
return this.signature != null;
}
public boolean isSignatureValid(PublicKey key) {
try {
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initVerify(key);
sig.update(this.value.getBytes());
return sig.verify(Base64.decode(this.signature.getBytes("UTF-8")));
} catch(Exception e) {
System.err.println("Failed to check profile property signature validity.");
e.printStackTrace();
}
return false;
}
}

View file

@ -0,0 +1,31 @@
package ch.spacebase.mc.auth;
public class ProfileTexture {
private String url;
public ProfileTexture(String url) {
this.url = url;
}
public String getUrl() {
return this.url;
}
public String getHash() {
String url = this.url.endsWith("/") ? this.url.substring(0, this.url.length() - 1) : this.url;
int slash = url.lastIndexOf("/");
int dot = url.lastIndexOf(".", slash);
return url.substring(slash + 1, dot != -1 ? dot : url.length());
}
public String toString() {
return "ProfileTexture{url=" + this.url + ", hash=" + this.getHash() + "}";
}
public static enum Type {
SKIN,
CAPE;
}
}

View file

@ -4,19 +4,39 @@ import ch.spacebase.mc.auth.exceptions.AuthenticationException;
import ch.spacebase.mc.auth.exceptions.AuthenticationUnavailableException;
import ch.spacebase.mc.auth.request.JoinServerRequest;
import ch.spacebase.mc.auth.response.HasJoinedResponse;
import ch.spacebase.mc.auth.response.MinecraftTexturesPayload;
import ch.spacebase.mc.auth.response.Response;
import ch.spacebase.mc.util.Base64;
import ch.spacebase.mc.util.IOUtils;
import ch.spacebase.mc.util.URLUtils;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
public class SessionService {
private static final String BASE_URL = "https://sessionserver.mojang.com/session/minecraft/";
private static final URL JOIN_URL = URLUtils.constantURL(BASE_URL + "join");
private static final URL CHECK_URL = URLUtils.constantURL(BASE_URL + "hasJoined");
private static final PublicKey SIGNATURE_KEY;
static {
try {
X509EncodedKeySpec spec = new X509EncodedKeySpec(IOUtils.toByteArray(SessionService.class.getResourceAsStream("/yggdrasil_session_pubkey.der")));
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
SIGNATURE_KEY = keyFactory.generatePublic(spec);
} catch(Exception var4) {
throw new ExceptionInInitializerError("Missing/invalid yggdrasil public key.");
}
}
public void joinServer(GameProfile profile, String authenticationToken, String serverId) throws AuthenticationException {
JoinServerRequest request = new JoinServerRequest(authenticationToken, profile.getId(), serverId);
URLUtils.makeRequest(JOIN_URL, request, Response.class);
@ -28,8 +48,19 @@ public class SessionService {
arguments.put("serverId", serverId);
URL url = URLUtils.concatenateURL(CHECK_URL, URLUtils.buildQuery(arguments));
try {
HasJoinedResponse e = URLUtils.makeRequest(url, null, HasJoinedResponse.class);
return e != null && e.getId() != null ? new GameProfile(e.getId(), user.getName()) : null;
HasJoinedResponse response = URLUtils.makeRequest(url, null, HasJoinedResponse.class);
if(response != null && response.getId() != null) {
GameProfile result = new GameProfile(response.getId(), user.getName());
if(response.getProperties() != null) {
for(ProfileProperty prop : response.getProperties()) {
result.getProperties().put(prop.getName(), prop);
}
}
return result;
} else {
return null;
}
} catch(AuthenticationUnavailableException var6) {
throw var6;
} catch(AuthenticationException var7) {
@ -37,6 +68,41 @@ public class SessionService {
}
}
public Map<String, ProfileTexture> getTextures(GameProfile profile) {
ProfileProperty textures = profile.getProperties().get("textures");
if(textures == null) {
return new HashMap<String, ProfileTexture>();
} else if(!textures.hasSignature()) {
System.err.println("Signature is missing from textures payload.");
return new HashMap<String, ProfileTexture>();
} else if(!textures.isSignatureValid(SIGNATURE_KEY)) {
System.err.println("Textures payload has been tampered with. (signature invalid)");
return new HashMap<String, ProfileTexture>();
} else {
MinecraftTexturesPayload result;
try {
String e = new String(Base64.decode(textures.getValue().getBytes("UTF-8")));
result = new Gson().fromJson(e, MinecraftTexturesPayload.class);
} catch(Exception e) {
System.err.println("Could not decode textures payload.");
e.printStackTrace();
return new HashMap<String, ProfileTexture>();
}
if(result.getProfileId() != null && result.getProfileId().equals(profile.getId())) {
if(result.getProfileName() != null && result.getProfileName().equals(profile.getName())) {
return result.getTextures() == null ? new HashMap<String, ProfileTexture>() : result.getTextures();
} else {
System.err.println("Decrypted textures payload was for another user. (expected name " + profile.getName() + " but was for " + result.getProfileName() + ")");
return new HashMap<String, ProfileTexture>();
}
} else {
System.err.println("Decrypted textures payload was for another user. (expected id " + profile.getId() + " but was for " + result.getProfileId() + ")");
return new HashMap<String, ProfileTexture>();
}
}
}
@Override
public String toString() {
return "SessionService{}";

View file

@ -7,6 +7,7 @@ import ch.spacebase.mc.auth.request.RefreshRequest;
import ch.spacebase.mc.auth.response.AuthenticationResponse;
import ch.spacebase.mc.auth.response.RefreshResponse;
import ch.spacebase.mc.auth.response.User;
import ch.spacebase.mc.auth.response.User.Property;
import ch.spacebase.mc.util.URLUtils;
import java.net.URL;
@ -14,7 +15,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@ -30,7 +30,7 @@ public class UserAuthentication {
private static final String STORAGE_KEY_ACCESS_TOKEN = "accessToken";
private String clientToken;
private Map<String, String> userProperties = new HashMap<String, String>();
private Map<String, List<String>> userProperties = new HashMap<String, List<String>>();
private String userId;
private String username;
private String password;
@ -38,6 +38,7 @@ public class UserAuthentication {
private boolean isOnline;
private List<GameProfile> profiles = new ArrayList<GameProfile>();
private GameProfile selectedProfile;
private UserType userType;
public UserAuthentication(String clientToken) {
if(clientToken == null) {
@ -67,8 +68,12 @@ public class UserAuthentication {
return this.selectedProfile;
}
public Map<String, String> getUserProperties() {
return this.isLoggedIn() ? Collections.unmodifiableMap(this.userProperties) : Collections.unmodifiableMap(new HashMap<String, String>());
public UserType getUserType() {
return this.isLoggedIn() ? (this.userType == null ? UserType.LEGACY : this.userType) : null;
}
public Map<String, List<String>> getUserProperties() {
return this.isLoggedIn() ? Collections.unmodifiableMap(this.userProperties) : Collections.unmodifiableMap(new HashMap<String, List<String>>());
}
public boolean isLoggedIn() {
@ -170,8 +175,14 @@ public class UserAuthentication {
AuthenticationRequest request = new AuthenticationRequest(this, this.username, this.password);
AuthenticationResponse response = URLUtils.makeRequest(ROUTE_AUTHENTICATE, request, AuthenticationResponse.class);
if(!response.getClientToken().equals(this.getClientToken())) {
throw new AuthenticationException("Server requested we change our client token. Don\'t know how to handle this!");
throw new AuthenticationException("Server requested we change our client token. Don't know how to handle this!");
} else {
if(response.getSelectedProfile() != null) {
this.userType = response.getSelectedProfile().isLegacy() ? UserType.LEGACY : UserType.MOJANG;
} else if(response.getAvailableProfiles() != null && response.getAvailableProfiles().length != 0) {
this.userType = response.getAvailableProfiles()[0].isLegacy() ? UserType.LEGACY : UserType.MOJANG;
}
if(response.getUser() != null && response.getUser().getId() != null) {
this.userId = response.getUser().getId();
} else {
@ -182,14 +193,7 @@ public class UserAuthentication {
this.accessToken = response.getAccessToken();
this.profiles = Arrays.asList(response.getAvailableProfiles());
this.selectedProfile = response.getSelectedProfile();
this.userProperties.clear();
if(response.getUser() != null && response.getUser().getProperties() != null) {
Iterator<User.Property> it = response.getUser().getProperties().iterator();
while(it.hasNext()) {
User.Property property = it.next();
this.userProperties.put(property.getKey(), property.getValue());
}
}
this.updateProperties(response.getUser());
}
}
}
@ -209,8 +213,14 @@ public class UserAuthentication {
RefreshRequest request = new RefreshRequest(this);
RefreshResponse response = URLUtils.makeRequest(ROUTE_REFRESH, request, RefreshResponse.class);
if(!response.getClientToken().equals(this.getClientToken())) {
throw new AuthenticationException("Server requested we change our client token. Don\'t know how to handle this!");
throw new AuthenticationException("Server requested we change our client token. Don't know how to handle this!");
} else {
if(response.getSelectedProfile() != null) {
this.userType = response.getSelectedProfile().isLegacy() ? UserType.LEGACY : UserType.MOJANG;
} else if(response.getAvailableProfiles() != null && response.getAvailableProfiles().length != 0) {
this.userType = response.getAvailableProfiles()[0].isLegacy() ? UserType.LEGACY : UserType.MOJANG;
}
if(response.getUser() != null && response.getUser().getId() != null) {
this.userId = response.getUser().getId();
} else {
@ -221,15 +231,7 @@ public class UserAuthentication {
this.accessToken = response.getAccessToken();
this.profiles = Arrays.asList(response.getAvailableProfiles());
this.selectedProfile = response.getSelectedProfile();
this.userProperties.clear();
if(response.getUser() != null && response.getUser().getProperties() != null) {
Iterator<User.Property> it = response.getUser().getProperties().iterator();
while(it.hasNext()) {
User.Property property = it.next();
this.userProperties.put(property.getKey(), property.getValue());
}
}
this.updateProperties(response.getUser());
}
}
}
@ -242,6 +244,7 @@ public class UserAuthentication {
this.accessToken = null;
this.profiles = null;
this.isOnline = false;
this.userType = null;
}
public void selectGameProfile(GameProfile profile) throws AuthenticationException {
@ -253,14 +256,14 @@ public class UserAuthentication {
RefreshRequest request = new RefreshRequest(this, profile);
RefreshResponse response = URLUtils.makeRequest(ROUTE_REFRESH, request, RefreshResponse.class);
if(!response.getClientToken().equals(this.getClientToken())) {
throw new AuthenticationException("Server requested we change our client token. Don\'t know how to handle this!");
throw new AuthenticationException("Server requested we change our client token. Don't know how to handle this!");
} else {
this.isOnline = true;
this.accessToken = response.getAccessToken();
this.selectedProfile = response.getSelectedProfile();
}
} else {
throw new IllegalArgumentException("Invalid profile \'" + profile + "\'");
throw new IllegalArgumentException("Invalid profile '" + profile + "'");
}
}
@ -269,4 +272,17 @@ public class UserAuthentication {
return "UserAuthentication{profiles=" + this.profiles + ", selectedProfile=" + this.getSelectedProfile() + ", username=" + this.username + ", isLoggedIn=" + this.isLoggedIn() + ", canPlayOnline=" + this.canPlayOnline() + ", accessToken=" + this.accessToken + ", clientToken=" + this.getClientToken() + "}";
}
private void updateProperties(User user) {
this.userProperties.clear();
if(user != null && user.getProperties() != null) {
for(Property property : user.getProperties()) {
if(this.userProperties.get(property.getKey()) == null) {
this.userProperties.put(property.getKey(), new ArrayList<String>());
}
this.userProperties.get(property.getKey()).add(property.getValue());
}
}
}
}

View file

@ -0,0 +1,8 @@
package ch.spacebase.mc.auth;
public enum UserType {
LEGACY,
MOJANG;
}

View file

@ -1,13 +1,21 @@
package ch.spacebase.mc.auth.response;
import java.util.List;
import ch.spacebase.mc.auth.ProfileProperty;
import ch.spacebase.mc.auth.response.Response;
public class HasJoinedResponse extends Response {
private String id;
private List<ProfileProperty> properties;
public String getId() {
return this.id;
}
public List<ProfileProperty> getProperties() {
return this.properties;
}
}

View file

@ -0,0 +1,30 @@
package ch.spacebase.mc.auth.response;
import java.util.Map;
import ch.spacebase.mc.auth.ProfileTexture;
public class MinecraftTexturesPayload {
private long timestamp;
private String profileId;
private String profileName;
private Map<String, ProfileTexture> textures;
public long getTimestamp() {
return this.timestamp;
}
public String getProfileId() {
return this.profileId;
}
public String getProfileName() {
return this.profileName;
}
public Map<String, ProfileTexture> getTextures() {
return this.textures;
}
}

View file

@ -33,6 +33,8 @@ import ch.spacebase.mc.protocol.packet.ingame.client.window.ClientEnchantItemPac
import ch.spacebase.mc.protocol.packet.ingame.client.window.ClientWindowActionPacket;
import ch.spacebase.mc.protocol.packet.ingame.client.world.ClientUpdateSignPacket;
import ch.spacebase.mc.protocol.packet.ingame.server.ServerChatPacket;
import ch.spacebase.mc.protocol.packet.ingame.server.ServerCombatPacket;
import ch.spacebase.mc.protocol.packet.ingame.server.ServerDifficultyPacket;
import ch.spacebase.mc.protocol.packet.ingame.server.ServerDisconnectPacket;
import ch.spacebase.mc.protocol.packet.ingame.server.ServerJoinGamePacket;
import ch.spacebase.mc.protocol.packet.ingame.server.ServerKeepAlivePacket;
@ -326,6 +328,8 @@ public class MinecraftProtocol extends PacketProtocol {
this.registerIncoming(62, ServerTeamPacket.class);
this.registerIncoming(63, ServerPluginMessagePacket.class);
this.registerIncoming(64, ServerDisconnectPacket.class);
this.registerIncoming(65, ServerDifficultyPacket.class);
this.registerIncoming(66, ServerCombatPacket.class);
this.registerOutgoing(0, ClientKeepAlivePacket.class);
this.registerOutgoing(1, ClientChatPacket.class);
@ -444,6 +448,8 @@ public class MinecraftProtocol extends PacketProtocol {
this.registerOutgoing(62, ServerTeamPacket.class);
this.registerOutgoing(63, ServerPluginMessagePacket.class);
this.registerOutgoing(64, ServerDisconnectPacket.class);
this.registerOutgoing(65, ServerDifficultyPacket.class);
this.registerOutgoing(66, ServerCombatPacket.class);
}
private void initClientStatus(Session session) {

View file

@ -3,8 +3,8 @@ package ch.spacebase.mc.protocol;
public class ProtocolConstants {
// General Constants
public static final String GAME_VERSION = "14w03b";
public static final int PROTOCOL_VERSION = 6;
public static final String GAME_VERSION = "14w04b";
public static final int PROTOCOL_VERSION = 8;
// General Key Constants
public static final String PROFILE_KEY = "profile";

View file

@ -24,12 +24,12 @@ public class ClientKeepAlivePacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.id = in.readInt();
this.id = in.readVarInt();
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.id);
out.writeVarInt(this.id);
}
@Override

View file

@ -24,7 +24,7 @@ public class ClientRequestPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.request = Request.values()[in.readByte()];
this.request = Request.values()[in.readUnsignedByte()];
}
@Override

View file

@ -8,36 +8,15 @@ import ch.spacebase.packetlib.packet.Packet;
public class ClientAnimationPacket implements Packet {
private int entityId;
private Animation animation;
@SuppressWarnings("unused")
private ClientAnimationPacket() {
}
public ClientAnimationPacket(int entityId, Animation animation) {
this.entityId = entityId;
this.animation = animation;
}
public int getEntityId() {
return this.entityId;
}
public Animation getAnimation() {
return this.animation;
public ClientAnimationPacket() {
}
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.animation = Animation.values()[in.readByte() - 1];
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeByte(this.animation.ordinal() + 1);
}
@Override
@ -45,8 +24,4 @@ public class ClientAnimationPacket implements Packet {
return false;
}
public static enum Animation {
SWING_ARM;
}
}

View file

@ -40,16 +40,16 @@ public class ClientEntityActionPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.action = Action.values()[in.readByte() - 1];
this.jumpBoost = in.readInt();
this.entityId = in.readVarInt();
this.action = Action.values()[in.readUnsignedByte() - 1];
this.jumpBoost = in.readVarInt();
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
out.writeByte(this.action.ordinal() + 1);
out.writeInt(this.jumpBoost);
out.writeVarInt(this.jumpBoost);
}
@Override

View file

@ -30,13 +30,13 @@ public class ClientEntityInteractPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.action = Action.values()[in.readByte()];
this.entityId = in.readVarInt();
this.action = Action.values()[in.readUnsignedByte()];
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
out.writeByte(this.action.ordinal());
}

View file

@ -44,16 +44,25 @@ public class ClientSteerVehiclePacket implements Packet {
public void read(NetInput in) throws IOException {
this.sideways = in.readFloat();
this.forward = in.readFloat();
this.jump = in.readBoolean();
this.dismount = in.readBoolean();
int flags = in.readUnsignedByte();
this.jump = (flags & 1) > 0;
this.dismount = (flags & 2) > 0;
}
@Override
public void write(NetOutput out) throws IOException {
out.writeFloat(this.sideways);
out.writeFloat(this.forward);
out.writeBoolean(this.jump);
out.writeBoolean(this.dismount);
byte flags = 0;
if(this.jump) {
flags = (byte) (flags | 1);
}
if(this.dismount) {
flags = (byte) (flags | 2);
}
out.writeByte(flags);
}
@Override

View file

@ -0,0 +1,91 @@
package ch.spacebase.mc.protocol.packet.ingame.server;
import java.io.IOException;
import ch.spacebase.packetlib.io.NetInput;
import ch.spacebase.packetlib.io.NetOutput;
import ch.spacebase.packetlib.packet.Packet;
public class ServerCombatPacket implements Packet {
private CombatState state;
private int entityId;
private int duration;
private int playerId;
private String message;
public ServerCombatPacket() {
this.state = CombatState.ENTER_COMBAT;
}
public ServerCombatPacket(int entityId, int duration) {
this.state = CombatState.END_COMBAT;
this.entityId = entityId;
this.duration = duration;
}
public ServerCombatPacket(int entityId, int playerId, String message) {
this.state = CombatState.ENTITY_DEAD;
this.entityId = entityId;
this.playerId = playerId;
this.message = message;
}
public CombatState getCombatState() {
return this.state;
}
public int getEntityId() {
return this.entityId;
}
public int getDuration() {
return this.duration;
}
public int getPlayerId() {
return this.playerId;
}
public String getMessage() {
return this.message;
}
@Override
public void read(NetInput in) throws IOException {
this.state = CombatState.values()[in.readUnsignedByte()];
if(this.state == CombatState.END_COMBAT) {
this.duration = in.readVarInt();
this.entityId = in.readInt();
} else if(this.state == CombatState.ENTITY_DEAD) {
this.playerId = in.readVarInt();
this.entityId = in.readInt();
this.message = in.readString();
}
}
@Override
public void write(NetOutput out) throws IOException {
out.writeByte(this.state.ordinal());
if(this.state == CombatState.END_COMBAT) {
out.writeVarInt(this.duration);
out.writeInt(this.entityId);
} else if(this.state == CombatState.ENTITY_DEAD) {
out.writeVarInt(this.playerId);
out.writeInt(this.entityId);
out.writeString(this.message);
}
}
@Override
public boolean isPriority() {
return false;
}
public static enum CombatState {
ENTER_COMBAT,
END_COMBAT,
ENTITY_DEAD;
}
}

View file

@ -38,14 +38,14 @@ public class ServerPlayerListEntryPacket implements Packet {
public void read(NetInput in) throws IOException {
this.name = in.readString();
this.online = in.readBoolean();
this.ping = in.readShort();
this.ping = in.readVarInt();
}
@Override
public void write(NetOutput out) throws IOException {
out.writeString(this.name);
out.writeBoolean(this.online);
out.writeShort(this.ping);
out.writeVarInt(this.ping);
}
@Override

View file

@ -30,14 +30,14 @@ public class ServerCollectItemPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.collectedEntityId = in.readInt();
this.collectorEntityId = in.readInt();
this.collectedEntityId = in.readVarInt();
this.collectorEntityId = in.readVarInt();
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.collectedEntityId);
out.writeInt(this.collectorEntityId);
out.writeVarInt(this.collectedEntityId);
out.writeVarInt(this.collectorEntityId);
}
@Override

View file

@ -24,17 +24,17 @@ public class ServerDestroyEntitiesPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityIds = new int[in.readByte()];
this.entityIds = new int[in.readVarInt()];
for(int index = 0; index < this.entityIds.length; index++) {
this.entityIds[index] = in.readInt();
this.entityIds[index] = in.readVarInt();
}
}
@Override
public void write(NetOutput out) throws IOException {
out.writeByte(this.entityIds.length);
out.writeVarInt(this.entityIds.length);
for(int entityId : this.entityIds) {
out.writeInt(entityId);
out.writeVarInt(entityId);
}
}

View file

@ -42,18 +42,18 @@ public class ServerEntityEffectPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
this.effect = Effect.values()[in.readByte()];
this.amplifier = in.readByte();
this.duration = in.readShort();
this.duration = in.readVarInt();
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
out.writeByte(this.effect.ordinal());
out.writeByte(this.amplifier);
out.writeShort(this.duration);
out.writeVarInt(this.duration);
}
@Override

View file

@ -38,14 +38,14 @@ public class ServerEntityEquipmentPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
this.slot = in.readShort();
this.item = NetUtil.readItem(in);
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
out.writeShort(this.slot);
NetUtil.writeItem(out, this.item);
}

View file

@ -26,13 +26,13 @@ public class ServerEntityHeadLookPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
this.headYaw = in.readByte() * 360 / 256f;
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
out.writeByte((byte) (this.headYaw * 256 / 360));
}

View file

@ -32,13 +32,13 @@ public class ServerEntityMetadataPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
this.metadata = NetUtil.readEntityMetadata(in);
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
NetUtil.writeEntityMetadata(out, this.metadata);
}

View file

@ -51,7 +51,7 @@ public class ServerEntityMovementPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
if(this.pos) {
this.moveX = in.readByte() / 32D;
this.moveY = in.readByte() / 32D;
@ -66,7 +66,7 @@ public class ServerEntityMovementPacket implements Packet {
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
if(this.pos) {
out.writeByte((int) (this.moveX * 32));
out.writeByte((int) (this.moveY * 32));

View file

@ -35,9 +35,9 @@ public class ServerEntityPropertiesPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
this.attributes = new ArrayList<Attribute>();
int length = in.readInt();
int length = in.readVarInt();
for(int index = 0; index < length; index++) {
String key = in.readString();
double value = in.readDouble();
@ -53,8 +53,8 @@ public class ServerEntityPropertiesPacket implements Packet {
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeInt(this.attributes.size());
out.writeVarInt(this.entityId);
out.writeVarInt(this.attributes.size());
for(Attribute attribute : this.attributes) {
out.writeString(attribute.getKey());
out.writeDouble(attribute.getValue());

View file

@ -10,18 +10,14 @@ public class ServerEntityRemoveEffectPacket implements Packet {
private int entityId;
private Effect effect;
private int amplifier;
private int duration;
@SuppressWarnings("unused")
private ServerEntityRemoveEffectPacket() {
}
public ServerEntityRemoveEffectPacket(int entityId, Effect effect, int amplifier, int duration) {
public ServerEntityRemoveEffectPacket(int entityId, Effect effect) {
this.entityId = entityId;
this.effect = effect;
this.amplifier = amplifier;
this.duration = duration;
}
public int getEntityId() {
@ -32,28 +28,16 @@ public class ServerEntityRemoveEffectPacket implements Packet {
return this.effect;
}
public int getAmplifier() {
return this.amplifier;
}
public int getDuration() {
return this.duration;
}
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
this.effect = Effect.values()[in.readByte()];
this.amplifier = in.readByte();
this.duration = in.readShort();
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
out.writeByte(this.effect.ordinal());
out.writeByte(this.amplifier);
out.writeShort(this.duration);
}
@Override

View file

@ -54,7 +54,7 @@ public class ServerEntityTeleportPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
this.x = in.readInt() / 32D;
this.y = in.readInt() / 32D;
this.z = in.readInt() / 32D;
@ -64,7 +64,7 @@ public class ServerEntityTeleportPacket implements Packet {
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
out.writeInt((int) (this.x * 32));
out.writeInt((int) (this.y * 32));
out.writeInt((int) (this.z * 32));

View file

@ -42,7 +42,7 @@ public class ServerEntityVelocityPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
this.motX = in.readShort() / 8000D;
this.motY = in.readShort() / 8000D;
this.motZ = in.readShort() / 8000D;
@ -50,7 +50,7 @@ public class ServerEntityVelocityPacket implements Packet {
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
out.writeShort((int) (this.motX * 8000));
out.writeShort((int) (this.motY * 8000));
out.writeShort((int) (this.motZ * 8000));

View file

@ -32,13 +32,13 @@ public class ServerPlayerUseBedPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readInt();
this.entityId = in.readVarInt();
this.position = NetUtil.readPosition(in);
}
@Override
public void write(NetOutput out) throws IOException {
out.writeInt(this.entityId);
out.writeVarInt(this.entityId);
NetUtil.writePosition(out, this.position);
}

View file

@ -37,15 +37,15 @@ public class ServerSetExperiencePacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.experience = in.readFloat();
this.level = in.readShort();
this.totalExperience = in.readShort();
this.level = in.readVarInt();
this.totalExperience = in.readVarInt();
}
@Override
public void write(NetOutput out) throws IOException {
out.writeFloat(this.experience);
out.writeShort(this.level);
out.writeShort(this.totalExperience);
out.writeVarInt(this.level);
out.writeVarInt(this.totalExperience);
}
@Override

View file

@ -37,14 +37,14 @@ public class ServerUpdateHealthPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.health = in.readFloat();
this.food = in.readShort();
this.food = in.readVarInt();
this.saturation = in.readFloat();
}
@Override
public void write(NetOutput out) throws IOException {
out.writeFloat(this.health);
out.writeShort(this.food);
out.writeVarInt(this.food);
out.writeFloat(this.saturation);
}

View file

@ -2,6 +2,8 @@ package ch.spacebase.mc.protocol.packet.ingame.server.entity.spawn;
import java.io.IOException;
import ch.spacebase.mc.protocol.data.game.Position;
import ch.spacebase.mc.util.NetUtil;
import ch.spacebase.packetlib.io.NetInput;
import ch.spacebase.packetlib.io.NetOutput;
import ch.spacebase.packetlib.packet.Packet;
@ -10,21 +12,17 @@ public class ServerSpawnPaintingPacket implements Packet {
private int entityId;
private Art art;
private int x;
private int y;
private int z;
private Position position;
private Direction direction;
@SuppressWarnings("unused")
private ServerSpawnPaintingPacket() {
}
public ServerSpawnPaintingPacket(int entityId, Art art, int x, int y, int z, Direction direction) {
public ServerSpawnPaintingPacket(int entityId, Art art, Position position, Direction direction) {
this.entityId = entityId;
this.art = art;
this.x = x;
this.y = y;
this.z = z;
this.position = position;
this.direction = direction;
}
@ -36,16 +34,8 @@ public class ServerSpawnPaintingPacket implements Packet {
return this.art;
}
public int getX() {
return this.x;
}
public int getY() {
return this.y;
}
public int getZ() {
return this.z;
public Position getPosition() {
return this.position;
}
public Direction getDirection() {
@ -56,20 +46,16 @@ public class ServerSpawnPaintingPacket implements Packet {
public void read(NetInput in) throws IOException {
this.entityId = in.readVarInt();
this.art = Art.valueOf(in.readString());
this.x = in.readInt();
this.y = in.readInt();
this.z = in.readInt();
this.direction = Direction.values()[in.readInt()];
this.position = NetUtil.readPosition(in);
this.direction = Direction.values()[in.readUnsignedByte()];
}
@Override
public void write(NetOutput out) throws IOException {
out.writeVarInt(this.entityId);
out.writeString(this.art.name());
out.writeInt(this.x);
out.writeInt(this.y);
out.writeInt(this.z);
out.writeInt(this.direction.ordinal());
NetUtil.writePosition(out, this.position);
out.writeByte(this.direction.ordinal());
}
@Override

View file

@ -2,6 +2,8 @@ package ch.spacebase.mc.protocol.packet.ingame.server.entity.spawn;
import java.io.IOException;
import ch.spacebase.mc.auth.GameProfile;
import ch.spacebase.mc.auth.ProfileProperty;
import ch.spacebase.mc.protocol.data.game.EntityMetadata;
import ch.spacebase.mc.util.NetUtil;
import ch.spacebase.packetlib.io.NetInput;
@ -11,8 +13,7 @@ import ch.spacebase.packetlib.packet.Packet;
public class ServerSpawnPlayerPacket implements Packet {
private int entityId;
private String uuid;
private String name;
private GameProfile profile;
private double x;
private double y;
private double z;
@ -25,10 +26,9 @@ public class ServerSpawnPlayerPacket implements Packet {
private ServerSpawnPlayerPacket() {
}
public ServerSpawnPlayerPacket(int entityId, String uuid, String name, double x, double y, double z, float yaw, float pitch, int currentItem, EntityMetadata metadata[]) {
public ServerSpawnPlayerPacket(int entityId, GameProfile profile, double x, double y, double z, float yaw, float pitch, int currentItem, EntityMetadata metadata[]) {
this.entityId = entityId;
this.uuid = uuid;
this.name = name;
this.profile = profile;
this.x = x;
this.y = y;
this.z = z;
@ -42,12 +42,8 @@ public class ServerSpawnPlayerPacket implements Packet {
return this.entityId;
}
public String getUUID() {
return this.uuid;
}
public String getName() {
return this.name;
public GameProfile getProfile() {
return this.profile;
}
public double getX() {
@ -81,8 +77,14 @@ public class ServerSpawnPlayerPacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.entityId = in.readVarInt();
this.uuid = in.readString();
this.name = in.readString();
this.profile = new GameProfile(in.readString(), in.readString());
for(int count = 0; count < in.readVarInt(); count++) {
String name = in.readString();
String value = in.readString();
String signature = in.readString();
this.profile.getProperties().put(name, new ProfileProperty(name, value, signature));
}
this.x = in.readInt() / 32D;
this.y = in.readInt() / 32D;
this.z = in.readInt() / 32D;
@ -95,8 +97,15 @@ public class ServerSpawnPlayerPacket implements Packet {
@Override
public void write(NetOutput out) throws IOException {
out.writeVarInt(this.entityId);
out.writeString(this.uuid);
out.writeString(this.name);
out.writeString(this.profile.getId());
out.writeString(this.profile.getName());
out.writeVarInt(this.profile.getProperties().size());
for(ProfileProperty property : this.profile.getProperties().values()) {
out.writeString(property.getName());
out.writeString(property.getValue());
out.writeString(property.getSignature());
}
out.writeInt((int) (this.x * 32));
out.writeInt((int) (this.y * 32));
out.writeInt((int) (this.z * 32));

View file

@ -9,16 +9,16 @@ import ch.spacebase.packetlib.packet.Packet;
public class ServerScoreboardObjectivePacket implements Packet {
private String name;
private String value;
private String displayName;
private Action action;
@SuppressWarnings("unused")
private ServerScoreboardObjectivePacket() {
}
public ServerScoreboardObjectivePacket(String name, String value, Action action) {
public ServerScoreboardObjectivePacket(String name, String displayName, Action action) {
this.name = name;
this.value = value;
this.displayName = displayName;
this.action = action;
}
@ -26,8 +26,8 @@ public class ServerScoreboardObjectivePacket implements Packet {
return this.name;
}
public String getValue() {
return this.value;
public String getDisplayName() {
return this.displayName;
}
public Action getAction() {
@ -37,14 +37,14 @@ public class ServerScoreboardObjectivePacket implements Packet {
@Override
public void read(NetInput in) throws IOException {
this.name = in.readString();
this.value = in.readString();
this.displayName = in.readString();
this.action = Action.values()[in.readByte()];
}
@Override
public void write(NetOutput out) throws IOException {
out.writeString(this.name);
out.writeString(this.value);
out.writeString(this.displayName);
out.writeByte(this.action.ordinal());
}

View file

@ -95,7 +95,7 @@ public class ServerTeamPacket implements Packet {
}
if(this.action == Action.CREATE || this.action == Action.ADD_PLAYER || this.action == Action.REMOVE_PLAYER) {
this.players = new String[in.readShort()];
this.players = new String[in.readVarInt()];
for(int index = 0; index < this.players.length; index++) {
this.players[index] = in.readString();
}
@ -114,7 +114,7 @@ public class ServerTeamPacket implements Packet {
}
if(this.action == Action.CREATE || this.action == Action.ADD_PLAYER || this.action == Action.REMOVE_PLAYER) {
out.writeShort(this.players.length);
out.writeVarInt(this.players.length);
for(String player : this.players) {
out.writeString(player);
}

View file

@ -8,60 +8,60 @@ import ch.spacebase.packetlib.packet.Packet;
public class ServerUpdateScorePacket implements Packet {
private String name;
private String entry;
private Action action;
private String scoreName;
private int scoreValue;
private String objective;
private int value;
@SuppressWarnings("unused")
private ServerUpdateScorePacket() {
}
public ServerUpdateScorePacket(String name) {
this.name = name;
public ServerUpdateScorePacket(String entry) {
this.entry = entry;
this.action = Action.REMOVE;
}
public ServerUpdateScorePacket(String name, String scoreName, int scoreValue) {
this.name = name;
this.scoreName = scoreName;
this.scoreValue = scoreValue;
public ServerUpdateScorePacket(String entry, String objective, int value) {
this.entry = entry;
this.objective = objective;
this.value = value;
this.action = Action.ADD_OR_UPDATE;
}
public String getScoreboardName() {
return this.name;
public String getEntry() {
return this.entry;
}
public Action getAction() {
return this.action;
}
public String getScoreName() {
return this.scoreName;
public String getObjective() {
return this.objective;
}
public int getScoreValue() {
return this.scoreValue;
public int getValue() {
return this.value;
}
@Override
public void read(NetInput in) throws IOException {
this.name = in.readString();
this.entry = in.readString();
this.action = Action.values()[in.readByte()];
if(this.action == Action.ADD_OR_UPDATE) {
this.scoreName = in.readString();
this.scoreValue = in.readInt();
this.objective = in.readString();
this.value = in.readVarInt();
}
}
@Override
public void write(NetOutput out) throws IOException {
out.writeString(this.name);
out.writeString(this.entry);
out.writeByte(this.action.ordinal());
if(this.action == Action.ADD_OR_UPDATE) {
out.writeString(this.scoreName);
out.writeInt(this.scoreValue);
out.writeString(this.objective);
out.writeVarInt(this.value);
}
}

View file

@ -1,5 +1,6 @@
package ch.spacebase.mc.util;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
@ -32,4 +33,17 @@ public class IOUtils {
return writer.toString();
}
public static byte[] toByteArray(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte buffer[] = new byte[DEFAULT_BUFFER_SIZE];
int n = 0;
while(-1 != (n = in.read(buffer))) {
out.write(buffer, 0, n);
}
in.close();
out.close();
return out.toByteArray();
}
}

View file

@ -22,11 +22,45 @@ import ch.spacebase.packetlib.io.NetOutput;
public class NetUtil {
private static final int[] EXPONENTS_OF_TWO = new int[] { 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 };
private static final int POSITION_X_SIZE = 1 + lastExponentOfTwo(nextPowerOfTwo(30000000));
private static final int POSITION_Z_SIZE = POSITION_X_SIZE;
private static final int POSITION_Y_SIZE = 64 - POSITION_X_SIZE - POSITION_Z_SIZE;
private static final int POSITION_Y_SHIFT = POSITION_Z_SIZE;
private static final int POSITION_X_SHIFT = POSITION_Y_SHIFT + POSITION_Y_SIZE;
private static final long POSITION_X_MASK = (1L << POSITION_X_SIZE) - 1;
private static final long POSITION_Y_MASK = (1L << POSITION_Y_SIZE) - 1;
private static final long POSITION_Z_MASK = (1L << POSITION_Z_SIZE) - 1;
/**
* An unfortunately necessary hack value for chunk data packet checks as to whether a packet contains skylight values or not.
*/
public static boolean hasSky = true;
private static int nextPowerOfTwo(int i) {
int minusOne = i - 1;
minusOne |= minusOne >> 1;
minusOne |= minusOne >> 2;
minusOne |= minusOne >> 4;
minusOne |= minusOne >> 8;
minusOne |= minusOne >> 16;
return minusOne + 1;
}
private static boolean isPowerOfTwo(int i) {
return i != 0 && (i & i - 1) == 0;
}
private static int nextExponentOfTwo(int i) {
int power = isPowerOfTwo(i) ? i : nextPowerOfTwo(i);
return EXPONENTS_OF_TWO[(int) (power * 125613361L >> 27) & 31];
}
public static int lastExponentOfTwo(int i) {
return nextExponentOfTwo(i) - (isPowerOfTwo(i) ? 0 : 1);
}
public static CompoundTag readNBT(NetInput in) throws IOException {
short length = in.readShort();
if(length < 0) {
@ -50,13 +84,15 @@ public class NetUtil {
}
public static Position readPosition(NetInput in) throws IOException {
return new Position(in.readInt(), in.readUnsignedByte(), in.readInt());
long val = in.readLong();
int x = (int) (val << 64 - POSITION_X_SHIFT - POSITION_X_SIZE >> 64 - POSITION_X_SIZE);
int y = (int) (val << 64 - POSITION_Y_SHIFT - POSITION_Y_SIZE >> 64 - POSITION_Y_SIZE);
int z = (int) (val << 64 - POSITION_Z_SIZE >> 64 - POSITION_Z_SIZE);
return new Position(x, y, z);
}
public static void writePosition(NetOutput out, Position pos) throws IOException {
out.writeInt(pos.getX());
out.writeByte(pos.getY());
out.writeInt(pos.getZ());
out.writeLong((pos.getX() & POSITION_X_MASK) << POSITION_X_SHIFT | (pos.getY() & POSITION_Y_MASK) << POSITION_Y_SHIFT | (pos.getZ() & POSITION_Z_MASK));
}
public static ItemStack readItem(NetInput in) throws IOException {

Binary file not shown.