mirror of
https://bitbucket.org/projectswg/pswgcommon.git
synced 2026-01-16 23:04:32 -05:00
Changed CustomizationString to use a resource file rather than clientdata, and replaced CustomizationVariable with an integer
This commit is contained in:
@@ -28,21 +28,17 @@ package com.projectswg.common.data.customization;
|
||||
|
||||
import com.projectswg.common.data.encodables.mongo.MongoData;
|
||||
import com.projectswg.common.data.encodables.mongo.MongoPersistable;
|
||||
import com.projectswg.common.data.swgfile.ClientFactory;
|
||||
import com.projectswg.common.data.swgfile.visitors.CustomizationIDManagerData;
|
||||
import com.projectswg.common.encoding.Encodable;
|
||||
import com.projectswg.common.network.NetBuffer;
|
||||
import com.projectswg.common.network.NetBufferStream;
|
||||
import com.projectswg.common.persistable.Persistable;
|
||||
import me.joshlarson.jlcommon.log.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Writer;
|
||||
import java.io.*;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
@@ -56,16 +52,54 @@ import java.util.function.Function;
|
||||
*/
|
||||
public class CustomizationString implements Encodable, Persistable, MongoPersistable {
|
||||
|
||||
private final CustomizationIDManagerData table;
|
||||
private final Map<String, CustomizationVariable> variables;
|
||||
private static final Map<String, Short> VAR_NAME_TO_ID = new HashMap<>();
|
||||
private static final Map<Short, String> VAR_ID_TO_NAME = new HashMap<>();
|
||||
|
||||
static {
|
||||
try (BufferedInputStream bis = new BufferedInputStream(CustomizationString.class.getResourceAsStream("customization_variables.sdb"))) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
String key = null;
|
||||
int c;
|
||||
while ((c = bis.read()) != -1) {
|
||||
switch (c) {
|
||||
case '\t':
|
||||
key = buffer.toString();
|
||||
buffer.setLength(0);
|
||||
break;
|
||||
//noinspection HardcodedLineSeparator
|
||||
case '\r':
|
||||
//noinspection HardcodedLineSeparator
|
||||
case '\n':
|
||||
if (buffer.length() <= 0)
|
||||
continue;
|
||||
assert key != null;
|
||||
VAR_NAME_TO_ID.put(key, Short.valueOf(buffer.toString()));
|
||||
VAR_ID_TO_NAME.put(Short.valueOf(buffer.toString()), key);
|
||||
buffer.setLength(0);
|
||||
break;
|
||||
default:
|
||||
buffer.append((char) c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (buffer.length() > 0) {
|
||||
assert key != null;
|
||||
VAR_NAME_TO_ID.put(key, Short.valueOf(buffer.toString()));
|
||||
VAR_ID_TO_NAME.put(Short.valueOf(buffer.toString()), key);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("could not load customization variables from resources", e);
|
||||
}
|
||||
}
|
||||
|
||||
private final Map<String, Integer> variables;
|
||||
|
||||
public CustomizationString() {
|
||||
this.table = (CustomizationIDManagerData) ClientFactory.getInfoFromFile("customization/customization_id_manager.iff");
|
||||
this.variables = Collections.synchronizedMap(new LinkedHashMap<>()); // Ordered and synchronized
|
||||
}
|
||||
|
||||
boolean isEmpty() {
|
||||
return variables.size() <= 0;
|
||||
return variables.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,7 +107,7 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
* @return the amount of characters that would require escaping to be valid UTF-8
|
||||
*/
|
||||
int valuesToEscape() {
|
||||
return (int) variables.values().stream().filter(CustomizationVariable::isReserved).count();
|
||||
return (int) variables.values().stream().filter(CustomizationString::isReserved).count();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,52 +115,44 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
stream.addByte(0);
|
||||
stream.addMap(variables, (entry) -> {
|
||||
stream.addAscii(entry.getKey());
|
||||
stream.addInt(entry.getValue().getValue()); // Awful coincidence
|
||||
stream.addInt(entry.getValue());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(NetBufferStream stream) {
|
||||
stream.getByte();
|
||||
stream.getList((i) -> {
|
||||
variables.put(stream.getAscii(), new CustomizationVariable(stream.getInt()));
|
||||
});
|
||||
stream.getList((i) -> variables.put(stream.getAscii(), stream.getInt()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveMongo(MongoData data) {
|
||||
data.putMap("variables", variables, Function.identity(), CustomizationVariable::getValue);
|
||||
data.putMap("variables", variables, Function.identity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readMongo(MongoData data) {
|
||||
variables.clear();
|
||||
variables.putAll(data.getMap("variables", Integer.class, CustomizationVariable::new));
|
||||
variables.putAll(data.getMap("variables", Integer.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a new {@link CustomizationVariable} in this {@code CustomizationString}.
|
||||
* @param name is the variable to set
|
||||
* @param value is the assigned value
|
||||
* @return previously assigned {@link CustomizationVariable} value for {@code variableName}
|
||||
*/
|
||||
public CustomizationVariable put(String name, CustomizationVariable value) {
|
||||
public Integer put(String name, int value) {
|
||||
return variables.put(name, value);
|
||||
}
|
||||
|
||||
public CustomizationVariable remove(String name) {
|
||||
public Integer remove(String name) {
|
||||
return variables.remove(name);
|
||||
}
|
||||
|
||||
public CustomizationVariable get(String name) {
|
||||
public Integer get(String name) {
|
||||
return variables.get(name);
|
||||
}
|
||||
|
||||
public Map<String, CustomizationVariable> getVariables() {
|
||||
public Map<String, Integer> getVariables() {
|
||||
return Collections.unmodifiableMap(variables);
|
||||
}
|
||||
|
||||
public void forEach(BiConsumer<? super String, ? super CustomizationVariable> consumer) {
|
||||
public void forEach(BiConsumer<? super String, ? super Integer> consumer) {
|
||||
variables.forEach(consumer);
|
||||
}
|
||||
|
||||
@@ -138,11 +164,11 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
public String toString() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (Entry<String, CustomizationVariable> e : variables.entrySet()) {
|
||||
for (Entry<String, Integer> e : variables.entrySet()) {
|
||||
if (!first)
|
||||
str.append(", ");
|
||||
first = false;
|
||||
str.append(e.getKey()).append('=').append(e.getValue().getValue());
|
||||
str.append(e.getKey()).append('=').append(e.getValue());
|
||||
}
|
||||
return str.toString();
|
||||
}
|
||||
@@ -169,7 +195,7 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
|
||||
for (short i = 0; i < variableCount; i++) {
|
||||
short variableId = (short) codePoints[position++];
|
||||
String variableName = table.getVariableName(variableId);
|
||||
String variableName = VAR_ID_TO_NAME.get(variableId);
|
||||
|
||||
if (variableName == null) { // Variable ID matched no variable name.
|
||||
Log.w("Variable ID %d had no name associated", variableId);
|
||||
@@ -177,8 +203,7 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
continue;
|
||||
}
|
||||
|
||||
CustomizationVariable variable = new CustomizationVariable();
|
||||
|
||||
int variable;
|
||||
int current = codePoints[position++];
|
||||
|
||||
if (current == 0xFF) { // This marks an escaped character to follow
|
||||
@@ -186,10 +211,11 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
|
||||
switch (next) {
|
||||
case 0x01: // Value is 0
|
||||
variable.setValue(0);
|
||||
default:
|
||||
variable = 0;
|
||||
break;
|
||||
case 0x02: // Value is 255
|
||||
variable.setValue(0xFF);
|
||||
variable = 0xFF;
|
||||
break;
|
||||
case 0x03: // We shouldn't be meeting an end here. Malformed input.
|
||||
Log.w("Unexpected end of text in CustomizationString, assuming corruption!");
|
||||
@@ -197,7 +223,7 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
variable.setValue(current);
|
||||
variable = current;
|
||||
}
|
||||
|
||||
variables.put(variableName, variable);
|
||||
@@ -210,7 +236,7 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
}
|
||||
|
||||
int escapeFlag = codePoints[position++];
|
||||
int endOfText = codePoints[position++];
|
||||
int endOfText = codePoints[position];
|
||||
|
||||
if (escapeFlag == 0xFF && endOfText != 0x03) {
|
||||
Log.w("Invalid UTF-8 ending for CustomizationString, assuming corruption!");
|
||||
@@ -233,13 +259,12 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
out.write(variables.size());
|
||||
|
||||
variables.forEach((variableName, variable) -> {
|
||||
short combinedVariable = table.getVariableId(variableName);
|
||||
short combinedVariable = VAR_NAME_TO_ID.get(variableName);
|
||||
|
||||
try {
|
||||
writer.write(combinedVariable); // Put variable
|
||||
int value = variable.getValue();
|
||||
|
||||
switch (value) {
|
||||
switch (variable) {
|
||||
case 0x00:
|
||||
writer.write(0xFF); // Escape
|
||||
writer.write(0x01); // Put variable value
|
||||
@@ -249,7 +274,7 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
writer.write(0x02); // Put variable value
|
||||
break;
|
||||
default:
|
||||
writer.write(value);
|
||||
writer.write(variable);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@@ -291,4 +316,14 @@ public class CustomizationString implements Encodable, Persistable, MongoPersist
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param value the value to check
|
||||
* @return {@code true} if the value is reserved by UTF-8 and escaping it
|
||||
* would be necessary for proper compatibility.
|
||||
*/
|
||||
private static boolean isReserved(int value) {
|
||||
return value == 0x00 || value == 0xFF;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/***********************************************************************************
|
||||
* Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
|
||||
* *
|
||||
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
|
||||
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
|
||||
* Our goal is to create an emulator which will provide a server for players to *
|
||||
* continue playing a game similar to the one they used to play. We are basing *
|
||||
* it on the final publish of the game prior to end-game events. *
|
||||
* *
|
||||
* This file is part of PSWGCommon. *
|
||||
* *
|
||||
* --------------------------------------------------------------------------------*
|
||||
* *
|
||||
* PSWGCommon is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Affero General Public License as *
|
||||
* published by the Free Software Foundation, either version 3 of the *
|
||||
* License, or (at your option) any later version. *
|
||||
* *
|
||||
* PSWGCommon is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU Affero General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Affero General Public License *
|
||||
* along with PSWGCommon. If not, see <http://www.gnu.org/licenses/>. *
|
||||
***********************************************************************************/
|
||||
package com.projectswg.common.data.customization;
|
||||
|
||||
public class CustomizationVariable {
|
||||
|
||||
private int value;
|
||||
|
||||
public CustomizationVariable() {
|
||||
}
|
||||
|
||||
public CustomizationVariable(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return {@code true} if the value is reserved by UTF-8 and escaping it
|
||||
* would be necessary for proper compatibility.
|
||||
*/
|
||||
boolean isReserved() {
|
||||
return value == 0x00 || value == 0xFF;
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "CustomizationVariable["+value+']';
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import com.projectswg.common.data.swgfile.IffNode;
|
||||
import com.projectswg.common.data.swgfile.SWGFile;
|
||||
import me.joshlarson.jlcommon.log.Log;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -68,6 +69,10 @@ public class CustomizationIDManagerData extends ClientData {
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Short> getCustomizationVariables() {
|
||||
return Collections.unmodifiableMap(customizationVariables);
|
||||
}
|
||||
|
||||
/**
|
||||
* IDs are not zero-indexed - the first ID is 1, not 0.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
private/alternate_shader_blade 118
|
||||
/private/index_accel_max 90
|
||||
/private/index_accel_min 91
|
||||
/private/index_age 36
|
||||
/private/index_auto_level 97
|
||||
/private/index_banking 88
|
||||
/private/index_color_0 34
|
||||
/private/index_color_107 70
|
||||
/private/index_color_1 2
|
||||
/private/index_color_2 1
|
||||
/private/index_color_3 29
|
||||
/private/index_color_4 49
|
||||
/private/index_color_5 65
|
||||
/private/index_color_blade 74
|
||||
/private/index_color_eye 60
|
||||
/private/index_color_eyebrow 64
|
||||
/private/index_color_eyeshadow 44
|
||||
/private/index_color_facial_hair 31
|
||||
/private/index_color_fur 75
|
||||
/private/index_color_hair 50
|
||||
/private/index_color_lips 46
|
||||
/private/index_color_pattern 59
|
||||
/private/index_color_patterns 99
|
||||
/private/index_color_skin_1 117
|
||||
/private/index_color_skin_2 116
|
||||
/private/index_color_skin 42
|
||||
/private/index_color_tat 55
|
||||
/private/index_color_tattoo 54
|
||||
/private/index_color_test 69
|
||||
/private/index_damp_height 93
|
||||
/private/index_damp_pitch 98
|
||||
/private/index_damp_roll 96
|
||||
/private/index_decel 92
|
||||
/private/index_glide 85
|
||||
/private/index_hover_height 89
|
||||
/private/index_patterns 101
|
||||
/private/index_skin 119
|
||||
/private/index_slope_mod 95
|
||||
/private/index_speed_max 87
|
||||
/private/index_speed_min 120
|
||||
/private/index_strafe 121
|
||||
/private/index_style_beard_2 103
|
||||
/private/index_style_beard 35
|
||||
/private/index_style_eyebrow 30
|
||||
/private/index_style_eyebrows 68
|
||||
/private/index_style_eyeshadow 45
|
||||
/private/index_style_freckles 37
|
||||
/private/index_style_hair 100
|
||||
/private/index_style_tattoo 53
|
||||
/private/index_texture 102
|
||||
/private/index_texture_1 38
|
||||
/private/index_turn_rate_max 86
|
||||
/private/index_turn_rate_min 94
|
||||
/shared_owner/blend_asian_0 41
|
||||
/shared_owner/blend_brow_0 67
|
||||
/shared_owner/blend_brow_1 66
|
||||
/shared_owner/blend_cheeks_0 11
|
||||
/shared_owner/blend_cheeks_1 17
|
||||
/shared_owner/blend_chest_backpack 83
|
||||
/shared_owner/blend_chin_0 80
|
||||
/shared_owner/blend_chin_1 78
|
||||
/shared_owner/blend_chinsize_0 28
|
||||
/shared_owner/blend_chinsize_1 27
|
||||
/shared_owner/blend_ear_0 62
|
||||
/shared_owner/blend_ear_1 63
|
||||
/shared_owner/blend_ears_0 25
|
||||
/shared_owner/blend_ears_1 26
|
||||
/shared_owner/blend_eyedirection_0 12
|
||||
/shared_owner/blend_eyedirection_1 14
|
||||
/shared_owner/blend_eyeshape_0 33
|
||||
/shared_owner/blend_eyeshape_1 32
|
||||
/shared_owner/blend_eyesize_0 7
|
||||
/shared_owner/blend_eyesize_1 10
|
||||
/shared_owner/blend_eyeslant_0 77
|
||||
/shared_owner/blend_fat 5
|
||||
/shared_owner/blend_flat_chest 43
|
||||
/shared_owner/blend_head_0 114
|
||||
/shared_owner/blend_head_1 115
|
||||
/shared_owner/blend_headsize_0 57
|
||||
/shared_owner/blend_headsize_1 58
|
||||
/shared_owner/blend_jacket 111
|
||||
/shared_owner/blend_jacket_bandolier 84
|
||||
/shared_owner/blend_jacket_belt 82
|
||||
/shared_owner/blend_jacket_robe 113
|
||||
/shared_owner/blend_jaw_0 19
|
||||
/shared_owner/blend_jaw_1 18
|
||||
/shared_owner/blend_lipfullness_0 23
|
||||
/shared_owner/blend_lipfullness_1 24
|
||||
/shared_owner/blend_lipfulness_0 104
|
||||
/shared_owner/blend_lipfulness_1 105
|
||||
/shared_owner/blend_lipwidth_0 21
|
||||
/shared_owner/blend_lipwidth_1 22
|
||||
/shared_owner/blend_muscle 4
|
||||
/shared_owner/blend_nosedepth_0 8
|
||||
/shared_owner/blend_nosedepth_1 6
|
||||
/shared_owner/blend_noselength_0 13
|
||||
/shared_owner/blend_noselength_1 9
|
||||
/shared_owner/blend_nosesize_0 52
|
||||
/shared_owner/blend_nosesize_1 51
|
||||
/shared_owner/blend_nosewidth_0 15
|
||||
/shared_owner/blend_nosewidth_1 16
|
||||
/shared_owner/blend_robe 109
|
||||
/shared_owner/blend_robe_bandolier 81
|
||||
/shared_owner/blend_robe_belt 76
|
||||
/shared_owner/blend_sensor_0 39
|
||||
/shared_owner/blend_sensor_1 40
|
||||
/shared_owner/blend_skinny 3
|
||||
/shared_owner/index_color_0 56
|
||||
/shared_owner/index_color_1 73
|
||||
/shared_owner/index_color_2 79
|
||||
/shared_owner/index_color_3 72
|
||||
/shared_owner/index_color_4 71
|
||||
/shared_owner/index_color_pattern 48
|
||||
/shared_owner/index_color_skin 20
|
||||
/shared_owner/index_style_beard 110
|
||||
/shared_owner/index_style_beard_2 112
|
||||
/shared_owner/index_style_eyebrow 106
|
||||
/shared_owner/index_style_hair 108
|
||||
/shared_owner/index_texture 107
|
||||
/shared_owner/index_texture_1 47
|
||||
/shared_owner/muscle_fat 61
|
||||
@@ -35,32 +35,29 @@ public class TestCustomizationString {
|
||||
@Test
|
||||
public void testPut() {
|
||||
CustomizationString string = new CustomizationString();
|
||||
CustomizationVariable variable = new CustomizationVariable();
|
||||
String key = "test";
|
||||
|
||||
Assert.assertNull(string.put(key, variable)); // Nothing should be replaced because string's empty
|
||||
Assert.assertEquals(variable, string.put(key, null)); // Same key, so the variable we put earlier should be replaced
|
||||
Assert.assertNull(string.put(key, 0)); // Nothing should be replaced because string's empty
|
||||
Assert.assertEquals((Integer) 0, string.put(key, 1)); // Same key, so the variable we put earlier should be replaced
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemove() {
|
||||
CustomizationString string = new CustomizationString();
|
||||
CustomizationVariable variable = new CustomizationVariable();
|
||||
String key = "test";
|
||||
|
||||
string.put(key, variable);
|
||||
string.put(key, 0);
|
||||
|
||||
Assert.assertEquals(variable, string.remove(key)); // Same key, so the variable we put earlier should be returned
|
||||
Assert.assertEquals((Integer) 0, string.remove(key)); // Same key, so the variable we put earlier should be returned
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEmpty() {
|
||||
CustomizationString string = new CustomizationString();
|
||||
CustomizationVariable variable = new CustomizationVariable();
|
||||
String key = "test";
|
||||
|
||||
Assert.assertTrue(string.isEmpty());
|
||||
string.put(key, variable);
|
||||
string.put(key, 0);
|
||||
Assert.assertFalse(string.isEmpty());
|
||||
string.remove(key);
|
||||
Assert.assertTrue(string.isEmpty());
|
||||
@@ -69,19 +66,13 @@ public class TestCustomizationString {
|
||||
@Test
|
||||
public void testGetLength() {
|
||||
CustomizationString string = new CustomizationString();
|
||||
CustomizationVariable first = new CustomizationVariable();
|
||||
CustomizationVariable second = new CustomizationVariable();
|
||||
|
||||
first.setValue(7); // Requires no escaping
|
||||
second.setValue(0xFF); // Requires escaping
|
||||
|
||||
Assert.assertEquals(Short.BYTES, string.getLength()); // Should be an empty array at this point
|
||||
|
||||
string.put("first", first);
|
||||
string.put("first", 7);
|
||||
int expected = Short.BYTES + 7;
|
||||
Assert.assertEquals(expected, string.getLength());
|
||||
|
||||
string.put("second", second);
|
||||
string.put("second", 0xFF);
|
||||
expected += 4; // Two escape characters, an ID and a value
|
||||
Assert.assertEquals(expected, string.getLength());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user