Compare commits

..

No commits in common. "master" and "1.5.3" have entirely different histories.

43 changed files with 321 additions and 1729 deletions

View File

@ -10,8 +10,8 @@
<properties>
<javafx.version>1.8</javafx.version>
<jettyVersion>9.4.53.v20231009</jettyVersion>
<logback.version>1.3.12</logback.version>
<jettyVersion>9.4.48.v20220622</jettyVersion>
<logback.version>1.3.5</logback.version>
</properties>
<parent>
@ -209,7 +209,7 @@
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20231013</version>
<version>20190722</version>
</dependency>
<dependency>
<groupId>org.fxmisc.richtext</groupId>
@ -238,11 +238,14 @@
<artifactId>maven-artifact</artifactId>
<version>3.6.3</version>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>

View File

@ -5,7 +5,6 @@ import gearth.misc.Cacher;
import gearth.misc.UpdateChecker;
import gearth.misc.listenerpattern.ObservableObject;
import gearth.ui.GEarthController;
import gearth.ui.GEarthTrayIcon;
import gearth.ui.themes.Theme;
import gearth.ui.themes.ThemeFactory;
import gearth.ui.titlebar.TitleBarConfig;
@ -42,6 +41,7 @@ public class GEarth extends Application {
public void start(Stage primaryStage) throws Exception{
main = this;
stage = primaryStage;
FXMLLoader loader = new FXMLLoader(getClass().getResource("/gearth/ui/G-Earth.fxml"));
Parent root;
try {
@ -120,8 +120,7 @@ public class GEarth extends Application {
stage.getScene().getStylesheets().add(GEarth.class.getResource(String.format("/gearth/ui/themes/%s/styling.css", theme.internalName())).toExternalForm());
stage.getIcons().clear();
final Image image = new Image(GEarth.class.getResourceAsStream(String.format("/gearth/ui/themes/%s/logoSmall.png", theme.overridesLogo() ? theme.internalName() : defaultTheme.internalName())));
stage.getIcons().add(image);
stage.getIcons().add(new Image(GEarth.class.getResourceAsStream(String.format("/gearth/ui/themes/%s/logoSmall.png", theme.overridesLogo() ? theme.internalName() : defaultTheme.internalName()))));
stage.setTitle((theme.overridesTitle() ? theme.title() : defaultTheme.title()) + " " + GEarth.version);
controller.infoController.img_logo.setImage(new Image(GEarth.class.getResourceAsStream(
@ -132,11 +131,7 @@ public class GEarth extends Application {
)));
controller.infoController.version.setText(stage.getTitle());
// });
GEarthTrayIcon.updateOrCreate(image);
}
public GEarthController getController() {
return controller;
}
public static String[] args;

View File

@ -169,7 +169,9 @@ public abstract class ExtensionBase extends IExtension {
* The application got doubleclicked from the G-Earth interface. Doing something here is optional
*/
@Override
protected void onClick() {}
void onClick() {
}
@Override
protected ExtensionInfo getInfoAnnotations() {

View File

@ -67,7 +67,7 @@ public abstract class ExtensionForm extends ExtensionBase {
/**
* The application got doubleclicked from the G-Earth interface. Doing something here is optional
*/
public final void onClick(){
public void onClick(){
Platform.runLater(() -> {
primaryStage.show();
primaryStage.requestFocus();

View File

@ -1,9 +1,10 @@
package gearth.extensions.parsers;
import gearth.extensions.parsers.stuffdata.IStuffData;
import gearth.protocol.HPacket;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class HFloorItem implements IFurni {
private int id;
@ -11,15 +12,17 @@ public class HFloorItem implements IFurni {
private HPoint tile;
private HDirection facing;
private int category;
private int secondsToExpiration;
private int usagePolicy;
private int ownerId;
private String ownerName;
private IStuffData stuff;
private Object[] stuff;
private String sizeZ;
private Integer extra;
private String staticClass;
private String ignore1;
private Integer ignore2;
private String ignore3;
public HFloorItem(HPacket packet) {
id = packet.readInteger();
@ -31,10 +34,12 @@ public class HFloorItem implements IFurni {
tile = new HPoint(x, y, Double.parseDouble(packet.readString()));
sizeZ = packet.readString();
extra = packet.readInteger();
ignore1 = packet.readString();
ignore2 = packet.readInteger();
stuff = IStuffData.read(packet);
category = packet.readInteger();
stuff = HStuff.readData(packet, category);
secondsToExpiration = packet.readInteger();
usagePolicy = packet.readInteger();
@ -42,11 +47,13 @@ public class HFloorItem implements IFurni {
ownerId = packet.readInteger();
if (typeId < 0) {
staticClass = packet.readString();
ignore3 = packet.readString();
}
else {
staticClass = null;
ignore3 = null;
}
}
public void appendToPacket(HPacket packet) {
@ -69,14 +76,20 @@ public class HFloorItem implements IFurni {
packet.appendString(tile.getZ() + "");
// sizeZ = packet.readString();
packet.appendString(sizeZ);
// ignore1 = packet.readString();
packet.appendString(ignore1);
// extra = packet.readInteger();
packet.appendInt(extra);
// ignore2 = packet.readInteger();
packet.appendInt(ignore2);
// stuff = IStuffData.read(packet);
stuff.appendToPacket(packet);
// category = packet.readInteger();
packet.appendInt(category);
// stuff = HStuff.readData(packet, category);
for (Object object : stuff) {
packet.appendObject(object);
}
// secondsToExpiration = packet.readInteger();
packet.appendInt(secondsToExpiration);
@ -89,8 +102,8 @@ public class HFloorItem implements IFurni {
if (typeId < 0) {
// staticClass = packet.readString();
packet.appendString(staticClass);
// ignore3 = packet.readString();
packet.appendString(ignore3);
}
}
@ -161,9 +174,9 @@ public class HFloorItem implements IFurni {
return secondsToExpiration;
}
// public int getCategory() {
// return category;
// }
public int getCategory() {
return category;
}
public HDirection getFacing() {
return facing;
@ -173,7 +186,7 @@ public class HFloorItem implements IFurni {
return tile;
}
public IStuffData getStuff() {
public Object[] getStuff() {
return stuff;
}
@ -197,9 +210,9 @@ public class HFloorItem implements IFurni {
this.facing = facing;
}
// public void setCategory(int category) {
// this.category = category;
// }
public void setCategory(int category) {
this.category = category;
}
public void setSecondsToExpiration(int secondsToExpiration) {
this.secondsToExpiration = secondsToExpiration;
@ -213,7 +226,7 @@ public class HFloorItem implements IFurni {
this.ownerId = ownerId;
}
public void setStuff(IStuffData stuff) {
public void setStuff(Object[] stuff) {
this.stuff = stuff;
}
}

View File

@ -1,6 +1,5 @@
package gearth.extensions.parsers;
import gearth.extensions.parsers.stuffdata.IStuffData;
import gearth.protocol.HPacket;
public class HInventoryItem {
@ -9,7 +8,8 @@ public class HInventoryItem {
private int id;
private int typeId;
private HSpecialType specialType;
private IStuffData stuff;
private int category;
private Object[] stuff;
private boolean recyclable;
private boolean tradeable;
private boolean groupable;
@ -27,7 +27,8 @@ public class HInventoryItem {
id = packet.readInteger();
typeId = packet.readInteger();
specialType = HSpecialType.fromId(packet.readInteger());
stuff = IStuffData.read(packet);
category = packet.readInteger();
stuff = HStuff.readData(packet, category);
recyclable = packet.readBoolean();
tradeable = packet.readBoolean();
groupable = packet.readBoolean();
@ -48,7 +49,11 @@ public class HInventoryItem {
packet.appendInt(id);
packet.appendInt(typeId);
packet.appendInt(specialType.getId());
stuff.appendToPacket(packet);
packet.appendInt(category);
for (Object object : stuff) {
packet.appendObject(object);
}
packet.appendBoolean(recyclable);
packet.appendBoolean(tradeable);
@ -128,11 +133,19 @@ public class HInventoryItem {
this.typeId = typeId;
}
public IStuffData getStuff() {
public int getCategory() {
return category;
}
public void setCategory(int category) {
this.category = category;
}
public Object[] getStuff() {
return stuff;
}
public void setStuff(IStuffData stuff) {
public void setStuff(Object[] stuff) {
this.stuff = stuff;
}

View File

@ -0,0 +1,91 @@
package gearth.extensions.parsers;
import gearth.protocol.HPacket;
import java.util.ArrayList;
import java.util.List;
public class HStuff {
public static Object[] readData(HPacket packet, int category) {
List<Object> values = new ArrayList<>();
switch (category & 0xFF)
{
case 0: /* LegacyStuffData */ {
values.add(packet.readString());
break;
}
case 1: /* MapStuffData */ {
int count = packet.readInteger();
values.add(count);
for (int j = 0; j < count; j++)
{
values.add(packet.readString());
values.add(packet.readString());
}
break;
}
case 2: /* StringArrayStuffData */ {
int count = packet.readInteger();
values.add(count);
for (int j = 0; j < count; j++)
{
values.add(packet.readString());
}
break;
}
case 3: /* VoteResultStuffData */ {
values.add(packet.readString());
values.add(packet.readInteger());
break;
}
case 5: /* IntArrayStuffData */ {
int count = packet.readInteger();
values.add(count);
for (int j = 0; j < count; j++)
{
values.add(packet.readInteger());
}
break;
}
case 6: /* HighScoreStuffData */ {
values.add(packet.readString());
values.add(packet.readInteger());
values.add(packet.readInteger());
int count = packet.readInteger();
values.add(count);
for (int j = 0; j < count; j++)
{
int score = packet.readInteger();
values.add(score);
int subCount = packet.readInteger();
values.add(subCount);
for (int k = 0; k < subCount; k++)
{
values.add(packet.readString());
}
}
break;
}
case 7: /* CrackableStuffData */ {
values.add(packet.readString());
values.add(packet.readInteger());
values.add(packet.readInteger());
break;
}
}
if ((category & 0xFF00 & 0x100) > 0) {
values.add(packet.readInteger());
values.add(packet.readInteger());
}
return values.toArray();
}
}

View File

@ -13,7 +13,6 @@ public class HOffer {
private int priceInCredits;
private int priceInActivityPoints;
private int activityPointType;
private int priceInSilver;
private boolean giftable;
private List<HProduct> products = new ArrayList<>();
private int clubLevel;
@ -29,7 +28,6 @@ public class HOffer {
this.priceInCredits = packet.readInteger();
this.priceInActivityPoints = packet.readInteger();
this.activityPointType = packet.readInteger();
this.priceInSilver = packet.readInteger();
this.giftable = packet.readBoolean();
int productCount = packet.readInteger();
@ -67,10 +65,6 @@ public class HOffer {
return activityPointType;
}
public int getPriceInSilver() {
return priceInSilver;
}
public boolean isGiftable() {
return giftable;
}

View File

@ -1,173 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Stream;
/**
* Wrapper class to make IntArrayStuffData and StringArrayStuffData behave like a List
* @param <T> Value class
*/
public class ArrayStuffData<T> extends StuffDataBase implements List<T> {
protected List<T> values = new ArrayList<>();
public ArrayStuffData() {
super();
}
public ArrayStuffData(int uniqueSerialNumber, int uniqueSerialSize) {
super(uniqueSerialNumber, uniqueSerialSize);
}
@Override
public int size() {
return values.size();
}
@Override
public boolean isEmpty() {
return values.isEmpty();
}
@Override
public boolean contains(Object o) {
return values.contains(o);
}
@Override
public Iterator<T> iterator() {
return values.iterator();
}
@Override
public void forEach(Consumer<? super T> action) {
values.forEach(action);
}
@Override
public Object[] toArray() {
return values.toArray();
}
@Override
public <T1> T1[] toArray(T1[] a) {
return values.toArray(a);
}
@Override
public boolean add(T t) {
return values.add(t);
}
@Override
public boolean remove(Object o) {
return values.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return values.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends T> c) {
return values.addAll(c);
}
@Override
public boolean addAll(int index, Collection<? extends T> c) {
return values.addAll(index, c);
}
@Override
public boolean removeAll(Collection<?> c) {
return values.removeAll(c);
}
@Override
public boolean removeIf(Predicate<? super T> filter) {
return values.removeIf(filter);
}
@Override
public boolean retainAll(Collection<?> c) {
return values.retainAll(c);
}
@Override
public void replaceAll(UnaryOperator<T> operator) {
values.replaceAll(operator);
}
@Override
public void sort(Comparator<? super T> c) {
values.sort(c);
}
@Override
public void clear() {
values.clear();
}
@Override
public T get(int index) {
return values.get(index);
}
@Override
public T set(int index, T element) {
return values.set(index, element);
}
@Override
public void add(int index, T element) {
values.add(index, element);
}
@Override
public T remove(int index) {
return values.remove(index);
}
@Override
public int indexOf(Object o) {
return values.indexOf(o);
}
@Override
public int lastIndexOf(Object o) {
return values.lastIndexOf(o);
}
@Override
public ListIterator<T> listIterator() {
return values.listIterator();
}
@Override
public ListIterator<T> listIterator(int index) {
return values.listIterator(index);
}
@Override
public List<T> subList(int fromIndex, int toIndex) {
return values.subList(fromIndex, toIndex);
}
@Override
public Spliterator<T> spliterator() {
return values.spliterator();
}
@Override
public Stream<T> stream() {
return values.stream();
}
@Override
public Stream<T> parallelStream() {
return values.parallelStream();
}
}

View File

@ -1,73 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public class CrackableStuffData extends StuffDataBase {
public final static int IDENTIFIER = 7;
private String legacyString = "";
private int hits = 0;
private int target = 0;
protected CrackableStuffData() {}
public CrackableStuffData(String legacyString, int hits, int target) {
super();
this.legacyString = legacyString == null ? "" : legacyString;
this.hits = hits;
this.target = target;
}
public CrackableStuffData(int uniqueSerialNumber, int uniqueSerialSize, String legacyString, int hits, int target) {
super(uniqueSerialNumber, uniqueSerialSize);
this.legacyString = legacyString == null ? "" : legacyString;
this.hits = hits;
this.target = target;
}
@Override
protected void initialize(HPacket packet) {
this.legacyString = packet.readString();
this.hits = packet.readInteger();
this.target = packet.readInteger();
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendObjects(
this.legacyString,
this.hits,
this.target
);
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.legacyString;
}
@Override
public void setLegacyString(String legacyString) {
this.legacyString = legacyString;
}
public int getHits() {
return this.hits;
}
public void setHits(int hits) {
this.hits = hits;
}
public int getTarget() {
return this.target;
}
public void setTarget(int target) {
this.target = target;
}
}

View File

@ -1,26 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public class EmptyStuffData extends StuffDataBase {
public final static int IDENTIFIER = 4;
public EmptyStuffData() {
super();
}
public EmptyStuffData(int uniqueSerialNumber, int uniqueSerialSize) {
super(uniqueSerialNumber, uniqueSerialSize);
}
@Override
protected void initialize(HPacket packet) {
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
super.appendToPacket(packet);
}
}

View File

@ -1,53 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Arrays;
import java.util.Objects;
public class HighScoreData {
private int score;
private String[] users;
protected HighScoreData(HPacket packet) {
this.score = packet.readInteger();
int size = packet.readInteger();
this.users = new String[size];
for (int i = 0; i < size; i++) {
this.users[i] = packet.readString();
}
}
public HighScoreData(int score, String... users) {
super();
this.score = score;
this.users = Arrays.stream(users).filter(Objects::nonNull).toArray(String[]::new);
}
protected void appendToPacket(HPacket packet) {
packet.appendInt(this.score);
packet.appendInt(this.users.length);
for (String user : this.users) {
packet.appendString(user);
}
}
public int getScore() {
return this.score;
}
public void setScore(int score) {
this.score = score;
}
public String[] getUsers() {
return this.users.clone();
}
public void setUsers(String[] users) {
this.users = Arrays
.stream(users == null ? new String[0] : users)
.filter(Objects::nonNull)
.toArray(String[]::new);
}
}

View File

@ -1,99 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Arrays;
import java.util.Objects;
public class HighScoreStuffData extends StuffDataBase {
public final static int IDENTIFIER = 6;
private String legacyString = "";
// ['perteam', 'mostwins', 'classic', 'fastesttime', 'longesttime']
private int scoreType = 0;
// ['alltime', 'daily', 'weekly', 'monthly']
private int clearType = 0;
private HighScoreData[] entries = {};
protected HighScoreStuffData() {}
public HighScoreStuffData(String legacyString, int scoreType, int clearType, HighScoreData... entries) {
super();
this.legacyString = legacyString == null ? "" : legacyString;
this.scoreType = scoreType;
this.clearType = clearType;
this.entries = Arrays.stream(entries).filter(Objects::nonNull).toArray(HighScoreData[]::new);
}
public HighScoreStuffData(int uniqueSerialNumber, int uniqueSerialSize, String legacyString, int scoreType, int clearType, HighScoreData... entries) {
super(uniqueSerialNumber, uniqueSerialSize);
this.legacyString = legacyString == null ? "" : legacyString;
this.scoreType = scoreType;
this.clearType = clearType;
this.entries = Arrays.stream(entries).filter(Objects::nonNull).toArray(HighScoreData[]::new);
}
@Override
protected void initialize(HPacket packet) {
this.legacyString = packet.readString();
this.scoreType = packet.readInteger();
this.clearType = packet.readInteger();
int size = packet.readInteger();
this.entries = new HighScoreData[size];
for (int i = 0; i < size; i++) {
this.entries[i] = new HighScoreData(packet);
}
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendObjects(
this.legacyString,
this.scoreType,
this.clearType,
this.entries.length
);
for (HighScoreData entry : this.entries) {
entry.appendToPacket(packet);
}
}
@Override
public String getLegacyString() {
return this.legacyString;
}
@Override
public void setLegacyString(String legacyString) {
this.legacyString = legacyString;
}
public int getScoreType() {
return this.scoreType;
}
public void setScoreType(int scoreType) {
this.scoreType = scoreType;
}
public int getClearType() {
return this.clearType;
}
public void setClearType(int clearType) {
this.clearType = clearType;
}
public HighScoreData[] getEntries() {
return this.entries.clone();
}
public void setEntries(HighScoreData[] entries) {
this.entries = Arrays
.stream(entries == null ? new HighScoreData[0] : entries)
.filter(Objects::nonNull)
.toArray(HighScoreData[]::new);
}
}

View File

@ -1,66 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public interface IStuffData {
static IStuffData read(HPacket packet) {
int a = packet.readInteger();
StuffDataBase stuffData = null;
switch (a & 255) {
case LegacyStuffData.IDENTIFIER:
stuffData = new LegacyStuffData();
break;
case MapStuffData.IDENTIFIER:
stuffData = new MapStuffData();
break;
case StringArrayStuffData.IDENTIFIER:
stuffData = new StringArrayStuffData();
break;
case VoteResultStuffData.IDENTIFIER:
stuffData = new VoteResultStuffData();
break;
case EmptyStuffData.IDENTIFIER:
stuffData = new EmptyStuffData();
break;
case IntArrayStuffData.IDENTIFIER:
stuffData = new IntArrayStuffData();
break;
case HighScoreStuffData.IDENTIFIER:
stuffData = new HighScoreStuffData();
break;
case CrackableStuffData.IDENTIFIER:
stuffData = new CrackableStuffData();
break;
}
if (stuffData != null) {
stuffData.setFlags(a & 65280);
stuffData.initialize(packet);
} else {
throw new RuntimeException("Unknown stuffdata type");
}
return stuffData;
}
void appendToPacket(HPacket packet);
String getLegacyString();
void setLegacyString(String legacyString);
void setFlags(int flags);
int getFlags();
int getUniqueSerialNumber();
int getUniqueSerialSize();
void setUniqueSerialNumber(int uniqueSerialNumber);
void setUniqueSerialSize(int uniqueSerialSize);
int getRarityLevel();
int getState();
void setState(int state);
String getJSONValue(String key);
}

View File

@ -1,53 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Arrays;
// 5
public class IntArrayStuffData extends ArrayStuffData<Integer> {
public final static int IDENTIFIER = 5;
protected IntArrayStuffData() {}
public IntArrayStuffData(Integer... values) {
super();
this.values = Arrays.asList(values);
}
public IntArrayStuffData(int uniqueSerialNumber, int uniqueSerialSize, Integer... values) {
super(uniqueSerialNumber, uniqueSerialSize);
this.values = Arrays.asList(values);
}
@Override
protected void initialize(HPacket packet) {
int size = packet.readInteger();
this.clear();
for (int i = 0; i < size; i++) {
this.add(packet.readInteger());
}
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendInt(this.size());
for (int i : this) {
packet.appendInt(i);
}
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.size() > 0 ? String.valueOf(this.get(0)) : "";
}
@Override
public void setLegacyString(String legacyString) {
if (this.size() > 0)
this.set(0, Integer.parseInt(legacyString));
}
}

View File

@ -1,43 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public class LegacyStuffData extends StuffDataBase {
public final static int IDENTIFIER = 0;
private String legacyString;
protected LegacyStuffData() {}
public LegacyStuffData(String legacyString) {
this.legacyString = legacyString == null ? "" : legacyString;
}
public LegacyStuffData(int uniqueSerialNumber, int uniqueSerialSize, String legacyString) {
super(uniqueSerialNumber, uniqueSerialSize);
this.legacyString = legacyString == null ? "" : legacyString;
}
@Override
protected void initialize(HPacket packet) {
this.legacyString = packet.readString();
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendString(this.legacyString);
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.legacyString;
}
@Override
public void setLegacyString(String legacyString) {
this.legacyString = legacyString;
}
}

View File

@ -1,177 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Collectors;
public class MapStuffData extends StuffDataBase implements Map<String, String> {
public final static int IDENTIFIER = 1;
private Map<String, String> map = new HashMap<>();
protected MapStuffData() {}
public MapStuffData(HashMap<String, String> map) {
this.map = map == null ? new HashMap<>() : map.entrySet().stream().filter(e -> e.getValue() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, HashMap::new));
}
public MapStuffData(int uniqueSerialNumber, int uniqueSerialSize, HashMap<String, String> map) {
super(uniqueSerialNumber, uniqueSerialSize);
this.map = map == null ? new HashMap<>() : map.entrySet().stream().filter(e -> e.getValue() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (x, y) -> y, HashMap::new));
}
@Override
protected void initialize(HPacket packet) {
int size = packet.readInteger();
this.clear();
for (int i = 0; i < size; i++) {
this.put(packet.readString(), packet.readString());
}
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendInt(this.size());
for (Entry<String, String> entry : this.entrySet()) {
packet.appendObjects(
entry.getKey(),
entry.getValue() == null ? "" : entry.getValue()
);
}
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.getOrDefault("state", "");
}
@Override
public void setLegacyString(String legacyString) {
this.put("state", legacyString == null ? "" : legacyString);
}
@Override
public int size() {
return this.map.size();
}
@Override
public boolean isEmpty() {
return this.map.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return this.map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return this.map.containsValue(value);
}
@Override
public String get(Object key) {
return this.map.get(key);
}
@Override
public String put(String key, String value) {
return this.map.put(key, value);
}
@Override
public String remove(Object key) {
return this.map.remove(key);
}
@Override
public void putAll(Map<? extends String, ? extends String> m) {
this.map.putAll(m);
}
@Override
public void clear() {
this.map.clear();
}
@Override
public Set<String> keySet() {
return this.map.keySet();
}
@Override
public Collection<String> values() {
return this.map.values();
}
@Override
public Set<Entry<String, String>> entrySet() {
return this.map.entrySet();
}
@Override
public String getOrDefault(Object key, String defaultValue) {
return this.map.getOrDefault(key, defaultValue);
}
@Override
public void forEach(BiConsumer<? super String, ? super String> action) {
this.map.forEach(action);
}
@Override
public void replaceAll(BiFunction<? super String, ? super String, ? extends String> function) {
this.map.replaceAll(function);
}
@Override
public String putIfAbsent(String key, String value) {
return this.map.putIfAbsent(key, value);
}
@Override
public boolean remove(Object key, Object value) {
return this.map.remove(key, value);
}
@Override
public boolean replace(String key, String oldValue, String newValue) {
return this.map.replace(key, oldValue, newValue);
}
@Override
public String replace(String key, String value) {
return this.map.replace(key, value);
}
@Override
public String computeIfAbsent(String key, Function<? super String, ? extends String> mappingFunction) {
return this.map.computeIfAbsent(key, mappingFunction);
}
@Override
public String computeIfPresent(String key, BiFunction<? super String, ? super String, ? extends String> remappingFunction) {
return this.map.computeIfPresent(key, remappingFunction);
}
@Override
public String compute(String key, BiFunction<? super String, ? super String, ? extends String> remappingFunction) {
return this.map.compute(key, remappingFunction);
}
@Override
public String merge(String key, String value, BiFunction<? super String, ? super String, ? extends String> remappingFunction) {
return this.map.merge(key, value, remappingFunction);
}
}

View File

@ -1,55 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Collectors;
// 2
public class StringArrayStuffData extends ArrayStuffData<String> {
public final static int IDENTIFIER = 2;
protected StringArrayStuffData() {}
public StringArrayStuffData(String... array) {
this.values = Arrays.stream(array).filter(Objects::nonNull).collect(Collectors.toList());
}
public StringArrayStuffData(int uniqueSerialNumber, int uniqueSerialSize, String... array) {
super(uniqueSerialNumber, uniqueSerialSize);
this.values = Arrays.stream(array).filter(Objects::nonNull).collect(Collectors.toList());
}
@Override
protected void initialize(HPacket packet) {
int size = packet.readInteger();
this.clear();
for (int i = 0; i < size; i++) {
this.add(packet.readString());
}
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendInt(this.size());
for (String s : this) {
packet.appendString(s);
}
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.size() > 0 ? this.get(0) : "";
}
@Override
public void setLegacyString(String legacyString) {
if (this.size() > 0) {
this.set(0, legacyString == null ? "" : legacyString);
}
}
}

View File

@ -1,98 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
import org.json.JSONObject;
public abstract class StuffDataBase implements IStuffData {
private int flags = 0;
private int uniqueSerialNumber = 0;
private int uniqueSerialSize = 0;
protected StuffDataBase() {}
protected StuffDataBase(int uniqueSerialNumber, int uniqueSerialSize) {
this.uniqueSerialNumber = uniqueSerialNumber;
this.uniqueSerialSize = uniqueSerialSize;
flags = 256;
}
protected void initialize(HPacket packet) {
if ((flags & 256) > 0) {
this.uniqueSerialNumber = packet.readInteger();
this.uniqueSerialSize = packet.readInteger();
}
}
public void appendToPacket(HPacket packet) {
if ((flags & 256) > 0) {
packet.appendInt(this.uniqueSerialNumber);
packet.appendInt(this.uniqueSerialSize);
}
}
@Override
public String getLegacyString() {
return "";
}
@Override
public void setLegacyString(String legacyString) {}
@Override
public final void setFlags(int flags) {
this.flags = flags;
}
@Override
public final int getFlags() {
return this.flags;
}
@Override
public final int getUniqueSerialNumber() {
return this.uniqueSerialNumber;
}
@Override
public final int getUniqueSerialSize() {
return this.uniqueSerialSize;
}
@Override
public final void setUniqueSerialNumber(int uniqueSerialNumber) {
this.uniqueSerialNumber = uniqueSerialNumber;
}
@Override
public final void setUniqueSerialSize(int uniqueSerialSize) {
this.uniqueSerialSize = uniqueSerialSize;
}
@Override
public int getRarityLevel() {
return -1;
}
@Override
public final int getState() {
try {
return Integer.parseInt(getLegacyString());
} catch (Exception e) {
return -1;
}
}
@Override
public final void setState(int state) {
setLegacyString(String.valueOf(state));
}
@Override
public final String getJSONValue(String key) {
try {
return new JSONObject(getLegacyString()).getString(key);
} catch (Exception e) {
return "";
}
}
}

View File

@ -1,58 +0,0 @@
package gearth.extensions.parsers.stuffdata;
import gearth.protocol.HPacket;
public class VoteResultStuffData extends StuffDataBase {
public static final int IDENTIFIER = 3;
private String legacyString = "";
private int result = 0;
protected VoteResultStuffData() {}
public VoteResultStuffData(String legacyString, int result) {
this.legacyString = legacyString == null ? "" : legacyString;
this.result = result;
}
public VoteResultStuffData(int uniqueSerialNumber, int uniqueSerialSize, String legacyString, int result) {
super(uniqueSerialNumber, uniqueSerialSize);
this.legacyString = legacyString == null ? "" : legacyString;
this.result = result;
}
@Override
protected void initialize(HPacket packet) {
this.legacyString = packet.readString();
this.result = packet.readInteger();
super.initialize(packet);
}
@Override
public void appendToPacket(HPacket packet) {
packet.appendInt(IDENTIFIER | this.getFlags());
packet.appendObjects(
this.legacyString,
this.result
);
super.appendToPacket(packet);
}
@Override
public String getLegacyString() {
return this.legacyString;
}
@Override
public void setLegacyString(String legacyString) {
this.legacyString = legacyString == null ? "" : legacyString;
}
public int getResult() {
return this.result;
}
public void setResult(int result) {
this.result = result;
}
}

View File

@ -38,7 +38,7 @@ public class ProxyProviderFactory {
autoDetectHosts.add("game-it.habbo.com:30000");
autoDetectHosts.add("game-nl.habbo.com:30000");
autoDetectHosts.add("game-tr.habbo.com:30000");
autoDetectHosts.add("game-us.habbo.com:30000");
autoDetectHosts.add("game-us.habbo.com:38101");
autoDetectHosts.add("game-s2.habbo.com:30000");
List<Object> additionalCachedHotels = Cacher.getList(HOTELS_CACHE_KEY);

View File

@ -1,66 +0,0 @@
package gearth.protocol.connection.proxy.nitro;
import gearth.protocol.HMessage;
import gearth.protocol.connection.HState;
import gearth.protocol.connection.HStateSetter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class NitroConnectionState {
private static final Logger logger = LoggerFactory.getLogger(NitroConnectionState.class);
private final HStateSetter stateSetter;
private boolean aborting;
private boolean toServer;
private boolean toClient;
public NitroConnectionState(HStateSetter stateSetter) {
this.stateSetter = stateSetter;
}
public void setConnected(HMessage.Direction direction) {
if (direction == HMessage.Direction.TOCLIENT) {
this.toClient = true;
} else if (direction == HMessage.Direction.TOSERVER) {
this.toServer = true;
}
this.checkConnected();
}
public boolean isConnected() {
if (this.aborting) {
return false;
}
if (!this.toClient) {
return false;
}
if (!this.toServer) {
return false;
}
return true;
}
private void checkConnected() {
if (!this.isConnected()) {
return;
}
this.stateSetter.setState(HState.CONNECTED);
logger.info("Connected");
}
public void setAborting() {
this.aborting = true;
this.stateSetter.setState(HState.ABORTING);
logger.info("Aborting");
}
}

View File

@ -1,28 +0,0 @@
package gearth.protocol.connection.proxy.nitro;
import gearth.protocol.packethandler.nitro.NitroPacketHandler;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
public class NitroPacketQueue {
private final NitroPacketHandler packetHandler;
private final Queue<byte[]> packets;
public NitroPacketQueue(NitroPacketHandler packetHandler) {
this.packetHandler = packetHandler;
this.packets = new LinkedList<>();
}
public void enqueue(byte[] b) {
this.packets.add(b);
}
public synchronized void flush() throws IOException {
while (!this.packets.isEmpty()) {
this.packetHandler.act(this.packets.remove());
}
}
}

View File

@ -6,8 +6,6 @@ import gearth.protocol.connection.HProxySetter;
import gearth.protocol.connection.HState;
import gearth.protocol.connection.HStateSetter;
import gearth.protocol.connection.proxy.ProxyProvider;
import gearth.protocol.connection.proxy.nitro.http.NitroAuthority;
import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager;
import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxy;
import gearth.protocol.connection.proxy.nitro.http.NitroHttpProxyServerCallback;
import gearth.protocol.connection.proxy.nitro.websocket.NitroWebsocketProxy;
@ -15,6 +13,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.concurrent.atomic.AtomicBoolean;
public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCallback, StateChangeListener {
@ -33,14 +32,11 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa
private String originalCookies;
public NitroProxyProvider(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection) {
final NitroAuthority authority = new NitroAuthority();
final NitroCertificateSniffingManager certificateManager = new NitroCertificateSniffingManager(authority);
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.connection = connection;
this.nitroHttpProxy = new NitroHttpProxy(this, certificateManager);
this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this, certificateManager);
this.nitroHttpProxy = new NitroHttpProxy(this);
this.nitroWebsocketProxy = new NitroWebsocketProxy(proxySetter, stateSetter, connection, this);
this.abortLock = new AtomicBoolean();
}
@ -126,7 +122,7 @@ public class NitroProxyProvider implements ProxyProvider, NitroHttpProxyServerCa
public String replaceWebsocketServer(String configUrl, String websocketUrl) {
originalWebsocketUrl = websocketUrl;
return String.format("wss://127.0.0.1:%d", websocketPort);
return String.format("ws://127.0.0.1:%d", websocketPort);
}
@Override

View File

@ -20,31 +20,12 @@ public class NitroCertificateSniffingManager implements MitmManager {
private static final boolean DEBUG = false;
private final BouncyCastleSslEngineSource sslEngineSource;
private final Authority authority;
public NitroCertificateSniffingManager(Authority authority) {
this.authority = authority;
public NitroCertificateSniffingManager(Authority authority) throws RootCertificateException {
try {
sslEngineSource = new BouncyCastleSslEngineSource(authority, true, true, null);
} catch (final Exception e) {
throw new RuntimeException(new RootCertificateException("Errors during assembling root CA.", e));
}
}
public Authority getAuthority() {
return authority;
}
public SSLEngine websocketSslEngine(String commonName) {
final SubjectAlternativeNameHolder san = new SubjectAlternativeNameHolder();
san.addDomainName("localhost");
san.addIpAddress("127.0.0.1");
try {
return sslEngineSource.createCertForHost(commonName, san);
} catch (Exception e) {
throw new FakeCertificateException("Failed to create WebSocket certificate", e);
throw new RootCertificateException("Errors during assembling root CA.", e);
}
}

View File

@ -12,6 +12,8 @@ import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import org.littleshoot.proxy.HttpProxyServer;
import org.littleshoot.proxy.impl.DefaultHttpProxyServer;
import org.littleshoot.proxy.mitm.Authority;
import org.littleshoot.proxy.mitm.RootCertificateException;
import java.io.File;
import java.io.IOException;
@ -23,20 +25,20 @@ public class NitroHttpProxy {
private static final String ADMIN_WARNING_KEY = "admin_warning_dialog";
private static final AtomicBoolean SHUTDOWN_HOOK = new AtomicBoolean();
private final Authority authority;
private final NitroOsFunctions osFunctions;
private final NitroHttpProxyServerCallback serverCallback;
private final NitroCertificateSniffingManager certificateManager;
private HttpProxyServer proxyServer = null;
public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback, NitroCertificateSniffingManager certificateManager) {
public NitroHttpProxy(NitroHttpProxyServerCallback serverCallback) {
this.serverCallback = serverCallback;
this.certificateManager = certificateManager;
this.authority = new NitroAuthority();
this.osFunctions = NitroOsFunctionsFactory.create();
}
private boolean initializeCertificate() {
final File certificate = this.certificateManager.getAuthority().aliasFile(".pem");
final File certificate = this.authority.aliasFile(".pem");
// All good if certificate is already trusted.
if (this.osFunctions.isRootCertificateTrusted(certificate)) {
@ -78,7 +80,7 @@ public class NitroHttpProxy {
return false;
}
return this.osFunctions.installRootCertificate(this.certificateManager.getAuthority().aliasFile(".pem"));
return this.osFunctions.installRootCertificate(this.authority.aliasFile(".pem"));
}
/**
@ -98,28 +100,33 @@ public class NitroHttpProxy {
public boolean start() {
setupShutdownHook();
proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(NitroConstants.HTTP_PORT)
.withManInTheMiddle(this.certificateManager)
.withFiltersSource(new NitroHttpProxyFilterSource(serverCallback))
.withTransparent(true)
.start();
try {
proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(NitroConstants.HTTP_PORT)
.withManInTheMiddle(new NitroCertificateSniffingManager(authority))
.withFiltersSource(new NitroHttpProxyFilterSource(serverCallback))
.withTransparent(true)
.start();
if (!initializeCertificate()) {
proxyServer.stop();
if (!initializeCertificate()) {
proxyServer.stop();
System.out.println("Failed to initialize certificate");
System.out.println("Failed to initialize certificate");
return false;
}
if (!registerProxy()) {
proxyServer.stop();
System.out.println("Failed to register certificate");
return false;
}
return true;
} catch (RootCertificateException e) {
e.printStackTrace();
return false;
}
if (!registerProxy()) {
proxyServer.stop();
System.out.println("Failed to register certificate");
return false;
}
return true;
}
public void pause() {

View File

@ -16,24 +16,19 @@ import java.util.regex.Pattern;
public class NitroHttpProxyFilter extends HttpFiltersAdapter {
private static final String NitroConfigSearch = "socket.url";
private static final String NitroConfigSearch = "\"socket.url\"";
private static final String NitroClientSearch = "configurationUrls:";
private static final Pattern NitroConfigPattern = Pattern.compile("[\"']socket\\.url[\"']:(\\s+)?[\"'](wss?:.*?)[\"']", Pattern.MULTILINE);
private static final Pattern NitroConfigPattern = Pattern.compile("\"socket\\.url\":.?\"(wss?://.*?)\"", Pattern.MULTILINE);
// https://developers.cloudflare.com/fundamentals/get-started/reference/cloudflare-cookies/
private static final HashSet<String> CloudflareCookies = new HashSet<>(Arrays.asList(
"__cflb",
"__cf_bm",
"__cfseq",
"cf_ob_info",
"cf_use_ob",
"__cfwaitingroom",
"__cfruid",
"_cfuvid",
"cf_clearance",
"cf_chl_rc_i",
"cf_chl_rc_ni",
"cf_chl_rc_m"
"cf_clearance"
));
private static final String HeaderAcceptEncoding = "Accept-Encoding";
@ -100,11 +95,11 @@ public class NitroHttpProxyFilter extends HttpFiltersAdapter {
final Matcher matcher = NitroConfigPattern.matcher(responseBody);
if (matcher.find()) {
final String originalWebsocket = matcher.group(2).replace("\\/", "/");
final String originalWebsocket = matcher.group(1);
final String replacementWebsocket = callback.replaceWebsocketServer(this.url, originalWebsocket);
if (replacementWebsocket != null) {
responseBody = responseBody.replace(matcher.group(2), replacementWebsocket);
responseBody = responseBody.replace(originalWebsocket, replacementWebsocket);
responseModified = true;
}
}

View File

@ -1,20 +0,0 @@
package gearth.protocol.connection.proxy.nitro.http;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import javax.net.ssl.SSLEngine;
public class NitroSslContextFactory extends SslContextFactory.Server {
private final NitroCertificateSniffingManager certificateManager;
public NitroSslContextFactory(NitroCertificateSniffingManager certificateManager) {
this.certificateManager = certificateManager;
}
@Override
public SSLEngine newSSLEngine(String host, int port) {
System.out.printf("[NitroSslContextFactory] Creating SSLEngine for %s:%d%n", host, port);
return certificateManager.websocketSslEngine(host);
}
}

View File

@ -1,7 +1,6 @@
package gearth.protocol.connection.proxy.nitro.os;
import gearth.misc.OSValidator;
import gearth.protocol.connection.proxy.nitro.os.macos.NitroMacOS;
import gearth.protocol.connection.proxy.nitro.os.windows.NitroWindows;
import org.apache.commons.lang3.NotImplementedException;
@ -16,10 +15,7 @@ public final class NitroOsFunctionsFactory {
throw new NotImplementedException("unix nitro is not implemented yet");
}
if (OSValidator.isMac()) {
return new NitroMacOS();
}
throw new NotImplementedException("unsupported operating system");
throw new NotImplementedException("macOS nitro is not implemented yet");
}
}

View File

@ -1,96 +0,0 @@
package gearth.protocol.connection.proxy.nitro.os.macos;
import gearth.misc.RuntimeUtil;
import gearth.protocol.connection.proxy.nitro.os.NitroOsFunctions;
import java.io.File;
import java.io.IOException;
public class NitroMacOS implements NitroOsFunctions {
/**
* Semicolon separated hosts to ignore for proxying.
*/
private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;";
/**
* Checks if the certificate is trusted by the local machine.
* @param certificate Absolute path to the certificate.
* @return true if trusted
*/
@Override
public boolean isRootCertificateTrusted(File certificate) {
try {
final String output = RuntimeUtil.getCommandOutput(new String[] {"sh", "-c", "security verify-cert -c \"" + certificate.getAbsolutePath() + "\""});
return !output.contains("CSSMERR_TP_NOT_TRUSTED");
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean installRootCertificate(File certificate) {
final String certificatePath = certificate.getAbsolutePath();
try {
// Install the certificate
Process process = new ProcessBuilder("sh", "-c", "sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain \"" + certificatePath + "\"")
.inheritIO()
.start();
// Wait for the process to complete
int exitCode = process.waitFor();
if (exitCode != 0) {
System.out.printf("security add-trusted-cert command exited with exit code %d%n", exitCode);
return false;
}
return true;
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean registerSystemProxy(String host, int port) {
try {
final String[] ignoreHosts = PROXY_IGNORE.split(";");
// Enable proxy
Runtime.getRuntime().exec("networksetup -setwebproxy Wi-Fi " + host + " " + port);
Runtime.getRuntime().exec("networksetup -setsecurewebproxy Wi-Fi " + host + " " + port);
// Set proxy bypass domains
for (String ignoreHost : ignoreHosts) {
Runtime.getRuntime().exec("networksetup -setproxybypassdomains Wi-Fi " + ignoreHost);
}
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Override
public boolean unregisterSystemProxy() {
try {
// Disable proxy
Runtime.getRuntime().exec("networksetup -setwebproxystate Wi-Fi off");
Runtime.getRuntime().exec("networksetup -setsecurewebproxystate Wi-Fi off");
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
}

View File

@ -15,8 +15,7 @@ public class NitroWindows implements NitroOsFunctions {
/**
* Semicolon separated hosts to ignore for proxying.
*/
// habba.io;
private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;challenges.cloudflare.com;";
private static final String PROXY_IGNORE = "discord.com;discordapp.com;github.com;";
/**
* Checks if the certificate is trusted by the local machine.

View File

@ -1,6 +1,6 @@
package gearth.protocol.connection.proxy.nitro.websocket;
import org.eclipse.jetty.websocket.api.Session;
import javax.websocket.Session;
public interface NitroSession {

View File

@ -2,11 +2,8 @@ package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HConnection;
import gearth.protocol.HMessage;
import gearth.protocol.StateChangeListener;
import gearth.protocol.connection.*;
import gearth.protocol.connection.proxy.nitro.NitroConnectionState;
import gearth.protocol.connection.proxy.nitro.NitroConstants;
import gearth.protocol.connection.proxy.nitro.NitroPacketQueue;
import gearth.protocol.connection.proxy.nitro.NitroProxyProvider;
import gearth.protocol.packethandler.nitro.NitroPacketHandler;
import org.eclipse.jetty.websocket.jsr356.JsrSession;
@ -28,24 +25,22 @@ public class NitroWebsocketClient implements NitroSession {
private static final Logger logger = LoggerFactory.getLogger(NitroWebsocketClient.class);
private final HProxySetter proxySetter;
private final HStateSetter stateSetter;
private final HConnection connection;
private final NitroConnectionState state;
private final NitroProxyProvider proxyProvider;
private final NitroWebsocketServer server;
private final NitroPacketHandler packetHandler;
private final NitroPacketQueue packetQueue;
private final AtomicBoolean shutdownLock;
private JsrSession activeSession = null;
public NitroWebsocketClient(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) {
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.connection = connection;
this.state = new NitroConnectionState(stateSetter);
this.proxyProvider = proxyProvider;
this.server = new NitroWebsocketServer(connection, this, this.state);
this.server = new NitroWebsocketServer(connection, this);
this.packetHandler = new NitroPacketHandler(HMessage.Direction.TOSERVER, server, connection.getExtensionHandler(), connection.getTrafficObservables());
this.packetQueue = new NitroPacketQueue(this.packetHandler);
this.shutdownLock = new AtomicBoolean();
}
@ -53,27 +48,8 @@ public class NitroWebsocketClient implements NitroSession {
public void onOpen(Session session) throws Exception {
logger.info("WebSocket connection accepted");
// Setup state change listener
connection.getStateObservable().addListener(new StateChangeListener() {
@Override
public void stateChanged(HState oldState, HState newState) {
// Clean up when we don't need it anymore.
if ((oldState == HState.WAITING_FOR_CLIENT || newState == HState.NOT_CONNECTED) || newState == HState.ABORTING) {
connection.getStateObservable().removeListener(this);
}
// Process queue when connected.
try {
packetQueue.flush();
} catch (IOException e) {
logger.error("Failed to flush packet queue in state change listener", e);
}
}
});
activeSession = (JsrSession) session;
activeSession.getPolicy().setMaxBinaryMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
activeSession.getPolicy().setMaxTextMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
activeSession.setMaxBinaryMessageBufferSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
// Set proper headers to spoof being a real client.
final Map<String, List<String>> headers = new HashMap<>(activeSession.getUpgradeRequest().getHeaders());
@ -95,21 +71,12 @@ public class NitroWebsocketClient implements NitroSession {
);
proxySetter.setProxy(proxy);
state.setConnected(HMessage.Direction.TOSERVER);
stateSetter.setState(HState.CONNECTED);
}
@OnMessage
public void onMessage(byte[] b, Session session) throws IOException {
logger.debug("Received packet from browser");
// Enqueue all packets we receive to ensure we preserve correct packet order.
packetQueue.enqueue(b);
// Flush everything if we are connected.
// We also flush when connection state changes to connected.
if (state.isConnected()) {
packetQueue.flush();
}
packetHandler.act(b);
}
@OnClose
@ -127,7 +94,7 @@ public class NitroWebsocketClient implements NitroSession {
}
@Override
public org.eclipse.jetty.websocket.api.Session getSession() {
public Session getSession() {
return activeSession;
}
@ -165,7 +132,7 @@ public class NitroWebsocketClient implements NitroSession {
// Reset program state.
proxySetter.setProxy(null);
state.setAborting();
stateSetter.setState(HState.ABORTING);
}
}
}

View File

@ -4,8 +4,7 @@ import gearth.protocol.HConnection;
import gearth.protocol.connection.HProxySetter;
import gearth.protocol.connection.HStateSetter;
import gearth.protocol.connection.proxy.nitro.NitroProxyProvider;
import gearth.protocol.connection.proxy.nitro.http.NitroCertificateSniffingManager;
import gearth.protocol.connection.proxy.nitro.http.NitroSslContextFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
@ -22,37 +21,19 @@ public class NitroWebsocketProxy {
private final HStateSetter stateSetter;
private final HConnection connection;
private final NitroProxyProvider proxyProvider;
private final NitroCertificateSniffingManager certificateManager;
private final Server server;
private final int serverPort;
public NitroWebsocketProxy(HProxySetter proxySetter,
HStateSetter stateSetter,
HConnection connection,
NitroProxyProvider proxyProvider,
NitroCertificateSniffingManager certificateManager) {
public NitroWebsocketProxy(HProxySetter proxySetter, HStateSetter stateSetter, HConnection connection, NitroProxyProvider proxyProvider) {
this.proxySetter = proxySetter;
this.stateSetter = stateSetter;
this.connection = connection;
this.proxyProvider = proxyProvider;
this.certificateManager = certificateManager;
this.server = new Server();
this.serverPort = 0;
this.server = new Server(0);
}
public boolean start() {
try {
// Configure SSL.
final NitroSslContextFactory sslContextFactory = new NitroSslContextFactory(this.certificateManager);
final ServerConnector sslConnector = new ServerConnector(server, sslContextFactory);
sslConnector.setPort(this.serverPort);
// Add SSL to the server.
server.addConnector(sslConnector);
// Configure the WebSocket.
final ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");

View File

@ -2,34 +2,20 @@ package gearth.protocol.connection.proxy.nitro.websocket;
import gearth.protocol.HConnection;
import gearth.protocol.HMessage;
import gearth.protocol.connection.proxy.nitro.NitroConnectionState;
import gearth.protocol.connection.proxy.nitro.NitroConstants;
import gearth.protocol.packethandler.PacketHandler;
import gearth.protocol.packethandler.nitro.NitroPacketHandler;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketListener;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.jsr356.JsrExtension;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.*;
public class NitroWebsocketServer implements WebSocketListener, NitroSession {
public class NitroWebsocketServer extends Endpoint implements NitroSession {
private static final Logger logger = LoggerFactory.getLogger(NitroWebsocketServer.class);
@ -39,58 +25,80 @@ public class NitroWebsocketServer implements WebSocketListener, NitroSession {
"Sec-WebSocket-Version",
"Host",
"Connection",
"Upgrade",
"User-Agent", // Added by default
"Accept-Encoding", // Added by default
"Cache-Control", // Added by default
"Pragma" // Added by default
"Upgrade"
));
private final PacketHandler packetHandler;
private final NitroWebsocketClient client;
private final NitroConnectionState state;
private Session activeSession = null;
public NitroWebsocketServer(HConnection connection, NitroWebsocketClient client, NitroConnectionState state) {
public NitroWebsocketServer(HConnection connection, NitroWebsocketClient client) {
this.client = client;
this.state = state;
this.packetHandler = new NitroPacketHandler(HMessage.Direction.TOCLIENT, client, connection.getExtensionHandler(), connection.getTrafficObservables());
}
public void connect(String websocketUrl, Map<String, List<String>> clientHeaders) throws IOException {
try {
logger.info("Building origin websocket connection ({})", websocketUrl);
final WebSocketClient client = createWebSocketClient();
final ClientUpgradeRequest request = new ClientUpgradeRequest();
request.addExtensions("permessage-deflate");
clientHeaders.forEach((key, value) -> {
if (SKIP_HEADERS.contains(key)) {
return;
}
request.setHeader(key, value);
});
if (clientHeaders.containsKey("User-Agent")) {
final String realUserAgent = clientHeaders.get(HttpHeader.USER_AGENT.toString()).get(0);
final HttpField clientUserAgent = new HttpField(HttpHeader.USER_AGENT, realUserAgent);
client.getHttpClient().setUserAgentField(clientUserAgent);
}
logger.info("Connecting to origin websocket at {}", websocketUrl);
client.start();
client.connect(this, URI.create(websocketUrl), request);
} catch (Exception e) {
throw new IOException("Failed to start websocket client to origin " + websocketUrl, e);
ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create();
builder.extensions(Collections.singletonList(new JsrExtension(new ExtensionConfig("permessage-deflate;client_max_window_bits"))));
builder.configurator(new ClientEndpointConfig.Configurator() {
@Override
public void beforeRequest(Map<String, List<String>> headers) {
clientHeaders.forEach((key, value) -> {
if (SKIP_HEADERS.contains(key)) {
return;
}
headers.remove(key);
headers.put(key, value);
});
}
});
ClientEndpointConfig config = builder.build();
ContainerProvider.getWebSocketContainer().connectToServer(this, config, URI.create(websocketUrl));
logger.info("Connected to origin websocket");
} catch (DeploymentException e) {
throw new IOException("Failed to deploy websocket client", e);
}
}
@Override
public void onOpen(Session session, EndpointConfig config) {
this.activeSession = session;
this.activeSession.setMaxBinaryMessageBufferSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
this.activeSession.addMessageHandler(new MessageHandler.Whole<byte[]>() {
@Override
public void onMessage(byte[] message) {
try {
packetHandler.act(message);
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
@Override
public void onClose(Session session, CloseReason closeReason) {
// Hotel closed connection.
client.shutdownProxy();
}
@Override
public void onError(Session session, Throwable throwable) {
throwable.printStackTrace();
// Shutdown.
client.shutdownProxy();
}
@Override
public Session getSession() {
return activeSession;
@ -110,87 +118,10 @@ public class NitroWebsocketServer implements WebSocketListener, NitroSession {
try {
activeSession.close();
} catch (Exception e) {
} catch (IOException e) {
e.printStackTrace();
} finally {
activeSession = null;
}
}
@Override
public void onWebSocketBinary(byte[] bytes, int i, int i1) {
try {
packetHandler.act(bytes);
} catch (IOException e) {
logger.error("Failed to handle packet", e);
}
}
@Override
public void onWebSocketText(String s) {
logger.warn("Received text message from hotel");
}
@Override
public void onWebSocketClose(int i, String s) {
// Hotel closed connection.
client.shutdownProxy();
}
@Override
public void onWebSocketConnect(org.eclipse.jetty.websocket.api.Session session) {
activeSession = session;
state.setConnected(HMessage.Direction.TOCLIENT);
}
@Override
public void onWebSocketError(Throwable throwable) {
throwable.printStackTrace();
// Shutdown.
client.shutdownProxy();
}
private SSLContext createSSLContext() {
final TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}};
try {
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustAllCerts, new SecureRandom());
return sslContext;
} catch (Exception e) {
throw new RuntimeException("Failed to setup ssl context", e);
}
}
private HttpClient createHttpClient() {
final SslContextFactory.Client factory = new SslContextFactory.Client();
factory.setSslContext(createSSLContext());
return new HttpClient(factory);
}
private WebSocketClient createWebSocketClient() {
final WebSocketClient client = new WebSocketClient(createHttpClient());
client.getPolicy().setMaxBinaryMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
client.getPolicy().setMaxTextMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
return client;
}
}

View File

@ -134,10 +134,6 @@ public class MacOsHabboClient extends HabboClient {
private static byte[] hexStringToByteArray(String s) {
int len = s.length();
if (len % 2 == 1) {
s = "0" + s;
len += 1;
}
byte[] data = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)

View File

@ -6,17 +6,13 @@ import gearth.protocol.connection.proxy.nitro.websocket.NitroSession;
import gearth.protocol.packethandler.PacketHandler;
import gearth.protocol.packethandler.PayloadBuffer;
import gearth.services.extension_handler.ExtensionHandler;
import org.eclipse.jetty.websocket.api.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.Session;
import java.io.IOException;
import java.nio.ByteBuffer;
public class NitroPacketHandler extends PacketHandler {
private static final Logger logger = LoggerFactory.getLogger(NitroPacketHandler.class);
private final HMessage.Direction direction;
private final NitroSession session;
private final PayloadBuffer payloadBuffer;
@ -35,7 +31,6 @@ public class NitroPacketHandler extends PacketHandler {
final Session localSession = session.getSession();
if (localSession == null) {
logger.warn("Discarding {} bytes because the session for direction {} was null", buffer.length, this.direction);
return false;
}
@ -44,13 +39,7 @@ public class NitroPacketHandler extends PacketHandler {
buffer = buffer.clone();
}
try {
localSession.getRemote().sendBytes(ByteBuffer.wrap(buffer));
} catch (IOException e) {
logger.error("Error sending packet to nitro client", e);
return false;
}
localSession.getAsyncRemote().sendBinary(ByteBuffer.wrap(buffer));
return true;
}

View File

@ -357,7 +357,7 @@ public class UiLoggerController implements Initializable {
public void updateLoggerInfo() {
Platform.runLater(() -> {
viewIncoming.setKey(1, "ext.logger.state." + (chkViewIncoming.isSelected() ? "true" : "false"));
viewOutgoing.setKey(1, "ext.logger.state." + (chkViewOutgoing.isSelected() ? "true" : "false"));
viewIncoming.setKey(1, "ext.logger.state." + (chkViewOutgoing.isSelected() ? "true" : "false"));
autoScroll.setKey(1, "ext.logger.state." + (chkAutoscroll.isSelected() ? "true" : "false"));
filtered.setFormat("%s: " + filteredAmount);

View File

@ -1,105 +0,0 @@
package gearth.ui;
import gearth.GEarth;
import gearth.services.extension_handler.extensions.GEarthExtension;
import gearth.services.extension_handler.extensions.extensionproducers.ExtensionProducerFactory;
import gearth.services.extension_handler.extensions.implementations.network.NetworkExtensionServer;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
/**
* Adds a {@link TrayIcon} to the {@link SystemTray} for this G-Earth instance.
*
* @author Dorving
*/
public final class GEarthTrayIcon {
private static final String TO_FRONT_LABEL = "To front";
private static final String TO_BACK_LABEL = "To back";
private static PopupMenu menu;
public static void updateOrCreate(Image image) {
if (!SystemTray.isSupported())
return;
final NetworkExtensionServer server = ExtensionProducerFactory.getExtensionServer();
final BufferedImage awtImage = SwingFXUtils.fromFXImage(image, null);
final String appTitle = "G-Earth " + GEarth.version + " (" + server.getPort() + ")";
final Optional<TrayIcon> trayIcon = Stream.of(SystemTray.getSystemTray().getTrayIcons())
.filter(other -> Objects.equals(other.getToolTip(), appTitle))
.findFirst();
if (trayIcon.isPresent()) {
EventQueue.invokeLater(() -> trayIcon.get().setImage(awtImage));
} else {
menu = new PopupMenu();
menu.add(createToFrontOrBackMenuItem());
menu.addSeparator();
menu.addSeparator();
menu.add(createInstallMenuItem());
try {
SystemTray.getSystemTray().add(new TrayIcon(awtImage, appTitle, menu));
} catch (AWTException e) {
e.printStackTrace();
menu = null;
}
}
}
private static MenuItem createToFrontOrBackMenuItem() {
final MenuItem showMenuItem = new MenuItem(TO_FRONT_LABEL);
showMenuItem.addActionListener(e -> {
if (Objects.equals(showMenuItem.getLabel(), TO_FRONT_LABEL)) {
showMenuItem.setLabel(TO_BACK_LABEL);
Platform.runLater(() -> GEarth.main.getController().getStage().toFront());
} else {
showMenuItem.setLabel(TO_FRONT_LABEL);
Platform.runLater(() -> GEarth.main.getController().getStage().toBack());
}
});
return showMenuItem;
}
private static MenuItem createInstallMenuItem() {
final MenuItem showMenuItem = new MenuItem("Install Extension...");
showMenuItem.addActionListener(e ->
Optional.ofNullable(GEarth.main.getController())
.map(c -> c.extensionsController)
.ifPresent(c -> Platform.runLater(() -> {
final Stage stage = c.parentController.getStage();
final boolean isOnTop = stage.isAlwaysOnTop();
stage.setAlwaysOnTop(true); // bit of a hack to force stage to front
c.installBtnClicked(null);
stage.setAlwaysOnTop(isOnTop);
})));
return showMenuItem;
}
/**
* Adds the argued extension as a menu item to {@link #menu}.
*
* @param extension the {@link GEarthExtension} to add to the {@link #menu}.
*/
public static void addExtension(GEarthExtension extension) {
if (menu == null)
return;
final MenuItem menuItem = new MenuItem("Show "+extension.getTitle());
EventQueue.invokeLater(() -> menu.insert(menuItem, 2));
menuItem
.addActionListener(e -> Platform.runLater(() -> extension.getClickedObservable().fireEvent()));
extension.getDeletedObservable()
.addListener(() -> EventQueue.invokeLater(() -> menu.remove(menuItem)));
}
}

View File

@ -8,7 +8,6 @@ import gearth.services.extension_handler.extensions.implementations.network.exec
import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunner;
import gearth.services.extension_handler.extensions.implementations.network.executer.ExtensionRunnerFactory;
import gearth.services.g_python.GPythonShell;
import gearth.ui.GEarthTrayIcon;
import gearth.ui.SubForm;
import gearth.ui.subforms.extensions.logger.ExtensionLogger;
import gearth.ui.translations.LanguageBundle;
@ -19,7 +18,6 @@ import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import java.io.File;
@ -56,10 +54,9 @@ public class ExtensionsController extends SubForm {
protected void onParentSet() {
ExtensionItemContainerProducer producer = new ExtensionItemContainerProducer(extensioncontainer, scroller);
extensionHandler = new ExtensionHandler(getHConnection());
extensionHandler.getObservable().addListener((e -> Platform.runLater(() -> {
producer.extensionConnected(e);
GEarthTrayIcon.addExtension(e);
})));
extensionHandler.getObservable().addListener((e -> {
Platform.runLater(() -> producer.extensionConnected(e));
}));
//noinspection OptionalGetWithoutIsPresent
networkExtensionsProducer

View File

@ -147,7 +147,7 @@ alert.outdated.content.newversion=Uma nova versão do G-Earth foi encontrada
alert.outdated.content.update=Atualizar para a versão mais recente
### Alert - Invalid connection
alert.invalidconnection.content=Você entrou com informações de conexão inválidas, o G-Earth não conseguiu se conectar
alert.invalidconnection.content=Você entrou com inforções de conexão inválidas, o G-Earth não conseguiu se conectar
### Alert - Nitro root certificate
alert.rootcertificate.title=Instalação de certificado do root
@ -169,7 +169,7 @@ alert.somethingwentwrong.content=Algo inesperado aconteceu!\n\
para nossa página de Solução de problemas para resolver o ocorrido:
### Alert - Allow extension connection
alert.extconnection.content=Extensão "%s" tentou se conectar mas é desconhecida pelo G-Earth,\n\
alert.extconnection.content=Exntesão "%s" tentou se conectar mas é desconcida pelo G-Earth,\n\
aceitar essa conexão?
### Alert - G-Python error
@ -183,8 +183,8 @@ alert.gpythonerror.content=Algo inesperado aconteceu executando o terminal G-Pyt
## Internal extension
### Internal extension - G-ExtensionStore
ext.store.elapsedtime.second.single=segundo
ext.store.elapsedtime.second.multiple=segundos
ext.store.elapsedtime.second.single=secondo
ext.store.elapsedtime.second.multiple=secondos
ext.store.elapsedtime.minute.single=minuto
ext.store.elapsedtime.minute.multiple=minutos
ext.store.elapsedtime.hour.single=hora
@ -219,7 +219,7 @@ ext.store.extension.details.screenshot=Captura de tela
ext.store.extension.author.reputation=reputação
ext.store.extension.author.releases=lançamentos
ext.store.extension.warning.requirement=Aviso: o framework requer --url:additional installations-
ext.store.extension.warning.requirement=Aviso: o framework requer --url:instalações adicionais-
! IMPORTANT: the previous line has to end with the --url component like the english version
ext.store.extension.warning.unstable=Aviso: esta extensão foi marcada como instável!
@ -272,17 +272,17 @@ ext.store.categories.title=Categorias
ext.store.categories.description=Explore os diferentes tipos de extensões que o G-Earth tem a oferecer
ext.store.categories.contenttitle=Categorias
ext.store.fail.unzip=Erro ao descompactar
ext.store.fail.invalidurl=URL de extensão inválida
ext.store.fail.notavailable=Extensão não disponível no repositório
ext.store.fail.alreadyexists=Algo deu errado ao criar o diretório de extensão, a extensão já existe?
ext.store.fail.notfound=Extensão não foi encontrada
ext.store.fail.uninstall=Algo deu errado ao desinstalar a extensão, certifique-se de desconectar a extensão se ela ainda estiver em execução.
ext.store.fail.unzip=Error while unzipping
ext.store.fail.invalidurl=Invalid extension URL
ext.store.fail.notavailable=Extension not available in repository
ext.store.fail.alreadyexists=Something went wrong creating the extension directory, does the extension already exist?
ext.store.fail.notfound=Extension wasn't found
ext.store.fail.uninstall=Something went wrong with uninstalling the extension, make sure to disconnect the extension if it was still running.
ext.store.ordering.rating=Avaliação
ext.store.ordering.alphabetical=Alfabético
ext.store.ordering.lastupdated=Última atualização
ext.store.ordering.newreleases=Novos lançamentos
ext.store.ordering.rating=Rating
ext.store.ordering.alphabetical=Alphabetical
ext.store.ordering.lastupdated=Last updated
ext.store.ordering.newreleases=New releases
### Internal extension - Logger
ext.logger.menu.window=Janela
@ -307,7 +307,7 @@ ext.logger.menu.packets.displaydetails.byterep.legacy=Legado
ext.logger.menu.packets.displaydetails.byterep.hexdump=Hexdump
ext.logger.menu.packets.displaydetails.byterep.rawhex=Hex cru
ext.logger.menu.packets.displaydetails.byterep.none=Nenhum
ext.logger.menu.packets.displaydetails.message=Mensagem
ext.logger.menu.packets.displaydetails.message=Menssagem
ext.logger.menu.packets.displaydetails.message.name=Nome
ext.logger.menu.packets.displaydetails.message.hash=Hash
ext.logger.menu.packets.displaydetails.message.id=Id

View File

@ -36,10 +36,10 @@ Name | Language | Developers | Github
--- | --- | --- | --- |
G-Earth (Native) | Java | sirjonasxx | https://github.com/sirjonasxx/G-Earth
G-Python<sup>1</sup> | Python | sirjonasxx | https://github.com/sirjonasxx/G-Python
Geode | C# & Visual Basic | ArachisH, LilithRainbows | https://github.com/LilithRainbows/Geode
Xabbo | C# | b7 | https://github.com/b7c/Xabbo.GEarth
Xabbo scripter<sup>2</sup> | C# | b7 | https://github.com/b7c/Xabbo.Scripter
G-Node | Node.js | WiredSpast | https://github.com/WiredSpast/G-Node
G-Rust | Rust | WiredSpast | https://github.com/WiredSpast/G-Rust
GProgrammer<sup>3</sup> | Javascript | at15four2020 | https://github.com/at15four2020/GProgrammer/wiki
<sub>1: built-in in G-Earth through the [live scripting console](https://github.com/sirjonasxx/G-Earth/wiki/G-Python-qtConsole) </sub>

View File

@ -1,89 +1,24 @@
**NOTE: Currently supported browsers: ONLY Firefox and Chromium, works on Habbo AIR too**
# MacOS Installation Guide
In order to run G-Earth on macOs, you'll need to sign G-Mem (our memory searcher). This wiki page will cover that process.
G-Earth depends on an application named [G-Mem](https://github.com/sirjonasxx/G-Mem),
this application scans the memory contents of the Habbo client applicaton and extracts a cipher key
that is used to decrypt packets coming from the Habbo server.
First we'll create a certificate in order to sign it:
1. Open the Keychain Access application (You may find it inside Utilities).£
2. Select Certificate Assistant -> Create a Certificate.<br>
![](https://i.imgur.com/G6SS6ac.png)
3. Choose a name for the certificate (I'll use "gmem-cert") and set "Certificate Type" to "Code Signing". Also select "Let me override defaults" option.<br>
![](https://i.imgur.com/CAUI5Xi.png)
4. Click on "Continue" until "Specify a Location For The Certificate" appears, then set "Keychain" to "System".<br>
![](https://i.imgur.com/HwLDtmE.png)
5. Continue, the certificate will be created then.<br>
![](https://i.imgur.com/gYiKmZA.png)
There is a few steps u have to complete in order to get it to work on MacOS.
Once created, we are able to codesign gmem from Terminal<br>
`codesign -fs "gmem-cert" <G-Earth_Path>/G-Mem`
## Code-Sign G-Mem file and make it executable
![](https://i.imgur.com/xkryoJz.png)
### Certificate Creation
1. Open `Keychain Access` (press `⌘ + Enter`, type `KeyChain Access` to open it from spotlight)
2. Navigate from the top menu to `Keychain Access > Certificate Assistant > Create Certificate...`
![Screenshot 2023-03-30 at 14 36 47](https://user-images.githubusercontent.com/102377087/228837955-81182786-ac47-46e5-a5e2-1ca2e257751f.png)
3. In the `Create Your Certificate` do the following:
* Set `Name:` to `gmem-cert`
* Set `Cerificate Type:` to `Code Signing`
* Toggle the `Let me override default` button
![Screenshot 2023-03-30 at 14 40 28](https://user-images.githubusercontent.com/102377087/228838867-57e465bc-5b83-4b1a-a8cc-3dd6d1e95353.png)
5. Press `Continue` until you reach the `Specify a Location For The Certificate`, now do the following:
* Set `Keychain:` to `System`
![Screenshot 2023-03-30 at 14 42 50](https://user-images.githubusercontent.com/102377087/228839468-982365d9-925c-44cf-a87d-fc6c268d05c8.png)
6. Enter your login credentials when prompted and press `Done`
### Signing of G-Mem
1. Open `Terminal` (press `⌘ + Enter`, type `Terminal` to open it from spotlight)
2. Type `codesign -fs "gmem-cert" ` (do not press enter yet)
3. Drag the `G-Mem` file into your terminal window *(this will append the path to the terminal)*
Your terminal window should now resemble the following:
![Screenshot 2023-03-30 at 14 49 28](https://user-images.githubusercontent.com/102377087/228841126-77b0184b-4c7d-44e0-9f7c-56103a957a81.png)
4. Now press enter and enter your login credentials when prompted.
### Making G-Mem executable
1. Open `Terminal` (press `⌘ + Enter`, type `Terminal` to open it from spotlight)
2. Type `chmod 755 ` (do not press enter yet)
3. Drag the `G-Mem` file into your terminal window *(this will append the path to the terminal)*
Your terminal window should now resemble the following:
![Screenshot 2023-03-30 at 14 52 29](https://user-images.githubusercontent.com/102377087/228841918-3205014b-5de8-431d-ae4d-d10b8ceeed03.png)
4. Now press enter and verify the `Kind` of the `G-Mem` file is now `Unix Executable File`
![Screenshot 2023-03-30 at 14 54 15](https://user-images.githubusercontent.com/102377087/228842389-78ea857e-3414-43d0-8270-91f8185ab57f.png)
## Disabling SIP
Modern machines running MacOS have a security feature that shields of the memory of processes from other processes.
Depending on your machine you may have to disable SIP.
**For M1 macs it is required to disable SIP.**
### :warning: CAUTION :warning:
Turning off SIP allows any program with sudo privileges to modify memory contents of other processes. If you use pirated software, or other unverified apps, DO NOT DO THIS for your own safety! See the following stackoverflow post for some more info: https://apple.stackexchange.com/a/412281.
A guide for disabling SIP can be found here: https://developer.apple.com/documentation/security/disabling_and_enabling_system_integrity_protection
## Launching G-Earth
1. Open `Terminal` (press `⌘ + Enter`, type `Terminal` to open it from spotlight)
2. Type `sudo java -jar `
3. Drag the `G-Earth.jar` file into your terminal window *(this will append the path to the terminal)*
Your terminal window should now resemble the following:
![Screenshot 2023-03-30 at 15 00 59](https://user-images.githubusercontent.com/102377087/228843994-f7713373-9f19-49b0-b7e7-0645a16c4fce.png)
5. Press enter and fill in your password if prompted
## Troubleshooting
### 🚫 Apple cant check app for malicious software
In this case, you can grant an exception for a blocked app by clicking the `Open Anyway` button in Privacy & Security settings.
This button is available for about an hour after you try to open the app. Where it appears exactly depends on the OS version u are running,
more details about it can be found here: https://support.apple.com/en-gb/guide/mac-help/mchleab3a043/mac
### Other
If you experience any other issues and the [Troubleshooting Page](https://github.com/sirjonasxx/G-Earth/wiki/Troubleshooting) doesn't help,
it might be useful to have a look at the following issues: [#67](../issues/67) [#10](../issues/10)
Now you're ready to open G-Earth from Terminal<br>
`sudo java -jar G-Earth.jar`
If you experience any other issues and the troubleshooting page doesn't help, it might be useful to have a look at the following issues: [#67](../issues/67) [#10](../issues/10)