Compare commits

...

45 Commits

Author SHA1 Message Date
sirjonasxx
8a717ad73f
Merge pull request #167 from UnfamiliarLegacy/fix/nitro-packet-size
Set correct maximum on websocket policy
2023-07-11 22:24:05 +02:00
UnfamiliarLegacy
2c5c3f7db8 Set correct maximum on websocket policy 2023-06-28 22:59:28 +02:00
sirjonasxx
feb1f2a527
Merge pull request #155 from WiredSpast/master
Allow onClick method to be overridden in console extensions
2023-06-16 10:19:58 +02:00
WiredSpast
813fda45e5
Remove faulty changes 2023-06-16 07:57:28 +02:00
WiredSpast
b19d8baab4
Remove faulty changes 2023-06-16 07:56:08 +02:00
sirjonasxx
1add63115c
Merge pull request #158 from sirjonasxx/dependabot/maven/G-Earth/org.json-json-20230227
Bump json from 20190722 to 20230227 in /G-Earth
2023-06-16 07:16:54 +02:00
sirjonasxx
03271c344d
Merge pull request #165 from sirjonasxx/dependabot/maven/G-Earth/org.eclipse.jetty-jetty-server-9.4.51.v20230217
Bump jetty-server from 9.4.50.v20221201 to 9.4.51.v20230217 in /G-Earth
2023-06-16 07:15:13 +02:00
sirjonasxx
930f4d7641
Merge pull request #166 from LilithRainbows/master
Remove Geode from frameworks list
2023-06-16 07:14:51 +02:00
sirjonasxx
8ebd6ac189
Merge pull request #157 from dorving/patch-2
Fixed more typos in the MacOS installation guide
2023-06-16 07:11:40 +02:00
sirjonasxx
e9804afc41
Merge pull request #161 from dorving/trayicon
Added system tray icon support
2023-06-16 07:11:23 +02:00
sirjonasxx
ac9e802d50
Merge pull request #164 from UnfamiliarLegacy/fix/habbox
Improve Nitro reliability and bypass cloudflare challenge on french retro
2023-06-16 07:06:46 +02:00
Lilith
e1acccf303
Remove Geode from frameworks list
No longer mantained, xabbo is a better and updated alternative.
2023-06-14 22:00:27 -03:00
dependabot[bot]
223df57a6a
Bump jetty-server from 9.4.50.v20221201 to 9.4.51.v20230217 in /G-Earth
Bumps [jetty-server](https://github.com/eclipse/jetty.project) from 9.4.50.v20221201 to 9.4.51.v20230217.
- [Release notes](https://github.com/eclipse/jetty.project/releases)
- [Commits](https://github.com/eclipse/jetty.project/compare/jetty-9.4.50.v20221201...jetty-9.4.51.v20230217)

---
updated-dependencies:
- dependency-name: org.eclipse.jetty:jetty-server
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-06-13 23:10:32 +00:00
UnfamiliarLegacy
1579bed1ef Fix discarded packets 2023-06-07 19:59:55 +02:00
UnfamiliarLegacy
c7037c0441 Better nitro connection state management 2023-06-07 19:35:21 +02:00
UnfamiliarLegacy
df3aabf518 Add log message for packet discard 2023-06-07 19:19:50 +02:00
UnfamiliarLegacy
de39613091 Bypass user-agent based cloudflare challenge 2023-06-07 17:38:05 +02:00
dorving
f6626d1537 Added system tray icon support 2023-05-03 21:09:57 +02:00
WiredSpast
6db7c31bfa Attempt unity fix 2023-04-17 17:49:54 +02:00
WiredSpast
76acc8b391 Merge remote-tracking branch 'origin/master' 2023-04-17 17:47:25 +02:00
WiredSpast
bfc1d3a843 Attempt unity fix 2023-04-17 17:47:15 +02:00
WiredSpast
a8559cfdb1 Attempt unity fix 2023-04-17 17:36:41 +02:00
dependabot[bot]
a50fea88f4
Bump json from 20190722 to 20230227 in /G-Earth
Bumps [json](https://github.com/douglascrockford/JSON-java) from 20190722 to 20230227.
- [Release notes](https://github.com/douglascrockford/JSON-java/releases)
- [Changelog](https://github.com/stleary/JSON-java/blob/master/docs/RELEASES.md)
- [Commits](https://github.com/douglascrockford/JSON-java/commits)

---
updated-dependencies:
- dependency-name: org.json:json
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-04-14 17:55:25 +00:00
dorving
a34d16f1cc
Added troubleshooting step 2023-04-10 21:00:08 +02:00
dorving
cb786d4749
Fixed more typos in the MacOS installation guide 2023-04-10 20:32:59 +02:00
WiredSpast
3ee8f55766
Attempt onClick fix 2023-04-04 03:42:06 +02:00
WiredSpast
22f648ec6d
Attempt onClick fix 2023-04-04 03:40:02 +02:00
WiredSpast
ee73ae1d41
Attempt onClick fix 2023-04-04 03:37:54 +02:00
sirjonasxx
8de3d5cdd4
Merge pull request #151 from WiredSpast/master
Reformat StuffData
2023-03-30 22:12:26 +02:00
sirjonasxx
df12a945f4
Merge pull request #153 from dorving/patch-1
Update MacOS installation guide
2023-03-30 22:08:19 +02:00
dorving
1b72d600a3
Fixed typo 2023-03-30 15:09:08 +02:00
dorving
c06c4d41bb
Added link to guide for disabling SIP 2023-03-30 15:08:26 +02:00
dorving
150afc9ce1
Update macOs-Installation-guide.md
Updated steps with screenshots in English and added a guide for making the `G-Mem` file executable and disabling SIP.
2023-03-30 15:06:07 +02:00
sirjonasxx
a868ab512c
Merge pull request #152 from XePeleato/whatever
uilogger: Fix "View Outgoing" label behaviour
2023-03-24 09:14:18 +01:00
Eduardo Alonso
d96c468103 uilogger: Fix "View Outgoing" label behaviour 2023-03-23 19:03:09 +01:00
WiredSpast
4c7b924268 Deleted old HStuff class 2023-03-19 12:46:55 +01:00
WiredSpast
661ef48076 Added scoretype names and cleartype names in comments 2023-03-19 12:45:12 +01:00
WiredSpast
413c76915f Reformat StuffData 2023-03-19 12:14:58 +01:00
WiredSpast
aec5d6a137 Reformat StuffData 2023-03-19 11:52:01 +01:00
WiredSpast
43c15fed3e
Potential fix 2023-02-24 17:30:23 +01:00
sirjonasxx
6a9b1201ac
Merge pull request #147 from erikmoch/patch-1
Update messages_pt.properties
2023-02-21 01:34:28 +01:00
sirjonasxx
199fbed0dc
Merge pull request #149 from UnfamiliarLegacy/fix/nitro-ssl-2
Disable SSL certificate validation for Nitro websocket connection
2023-02-21 01:33:30 +01:00
UnfamiliarLegacy
8a880a13df Allow untrusted SSL certificates for Nitro 2023-02-13 05:03:56 +01:00
UnfamiliarLegacy
16749e9e96 Update jetty and drop javax.websocket-api 2023-02-13 03:32:34 +01:00
Moch
d8c5c8ebc0
Update messages_pt.properties
I carried out some orthographic corrections and translated new texts.
2023-02-05 00:20:17 -03:00
32 changed files with 1504 additions and 264 deletions

View File

@ -10,7 +10,7 @@
<properties>
<javafx.version>1.8</javafx.version>
<jettyVersion>9.4.48.v20220622</jettyVersion>
<jettyVersion>9.4.51.v20230217</jettyVersion>
<logback.version>1.3.5</logback.version>
</properties>
@ -209,7 +209,7 @@
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20190722</version>
<version>20230227</version>
</dependency>
<dependency>
<groupId>org.fxmisc.richtext</groupId>
@ -238,14 +238,11 @@
<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,6 +5,7 @@ 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;
@ -41,7 +42,6 @@ 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,7 +120,8 @@ 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();
stage.getIcons().add(new Image(GEarth.class.getResourceAsStream(String.format("/gearth/ui/themes/%s/logoSmall.png", theme.overridesLogo() ? theme.internalName() : defaultTheme.internalName()))));
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.setTitle((theme.overridesTitle() ? theme.title() : defaultTheme.title()) + " " + GEarth.version);
controller.infoController.img_logo.setImage(new Image(GEarth.class.getResourceAsStream(
@ -131,7 +132,11 @@ 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,9 +169,7 @@ public abstract class ExtensionBase extends IExtension {
* The application got doubleclicked from the G-Earth interface. Doing something here is optional
*/
@Override
void onClick() {
}
protected 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 void onClick(){
public final void onClick(){
Platform.runLater(() -> {
primaryStage.show();
primaryStage.requestFocus();

View File

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

View File

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

View File

@ -1,91 +0,0 @@
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

@ -0,0 +1,173 @@
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

@ -0,0 +1,73 @@
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

@ -0,0 +1,26 @@
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

@ -0,0 +1,53 @@
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

@ -0,0 +1,99 @@
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

@ -0,0 +1,66 @@
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

@ -0,0 +1,53 @@
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

@ -0,0 +1,43 @@
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

@ -0,0 +1,177 @@
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

@ -0,0 +1,55 @@
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

@ -0,0 +1,98 @@
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

@ -0,0 +1,58 @@
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

@ -0,0 +1,66 @@
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

@ -0,0 +1,28 @@
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

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

View File

@ -2,8 +2,11 @@ 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;
@ -25,22 +28,24 @@ 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.server = new NitroWebsocketServer(connection, this, this.state);
this.packetHandler = new NitroPacketHandler(HMessage.Direction.TOSERVER, server, connection.getExtensionHandler(), connection.getTrafficObservables());
this.packetQueue = new NitroPacketQueue(this.packetHandler);
this.shutdownLock = new AtomicBoolean();
}
@ -48,8 +53,27 @@ 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.setMaxBinaryMessageBufferSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
activeSession.getPolicy().setMaxBinaryMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
activeSession.getPolicy().setMaxTextMessageSize(NitroConstants.WEBSOCKET_BUFFER_SIZE);
// Set proper headers to spoof being a real client.
final Map<String, List<String>> headers = new HashMap<>(activeSession.getUpgradeRequest().getHeaders());
@ -71,12 +95,21 @@ public class NitroWebsocketClient implements NitroSession {
);
proxySetter.setProxy(proxy);
stateSetter.setState(HState.CONNECTED);
state.setConnected(HMessage.Direction.TOSERVER);
}
@OnMessage
public void onMessage(byte[] b, Session session) throws IOException {
packetHandler.act(b);
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();
}
}
@OnClose
@ -94,7 +127,7 @@ public class NitroWebsocketClient implements NitroSession {
}
@Override
public Session getSession() {
public org.eclipse.jetty.websocket.api.Session getSession() {
return activeSession;
}
@ -132,7 +165,7 @@ public class NitroWebsocketClient implements NitroSession {
// Reset program state.
proxySetter.setProxy(null);
stateSetter.setState(HState.ABORTING);
state.setAborting();
}
}
}

View File

@ -2,20 +2,34 @@ 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.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.jsr356.JsrExtension;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.websocket.*;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.net.URI;
import java.util.*;
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;
public class NitroWebsocketServer extends Endpoint implements NitroSession {
public class NitroWebsocketServer implements WebSocketListener, NitroSession {
private static final Logger logger = LoggerFactory.getLogger(NitroWebsocketServer.class);
@ -25,78 +39,56 @@ public class NitroWebsocketServer extends Endpoint implements NitroSession {
"Sec-WebSocket-Version",
"Host",
"Connection",
"Upgrade"
"Upgrade",
"User-Agent", // Added by default
"Accept-Encoding", // Added by default
"Cache-Control", // Added by default
"Pragma" // Added by default
));
private final PacketHandler packetHandler;
private final NitroWebsocketClient client;
private final NitroConnectionState state;
private Session activeSession = null;
public NitroWebsocketServer(HConnection connection, NitroWebsocketClient client) {
public NitroWebsocketServer(HConnection connection, NitroWebsocketClient client, NitroConnectionState state) {
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("Connecting to origin websocket at {}", websocketUrl);
logger.info("Building origin websocket connection ({})", websocketUrl);
ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create();
final WebSocketClient client = createWebSocketClient();
builder.extensions(Collections.singletonList(new JsrExtension(new ExtensionConfig("permessage-deflate;client_max_window_bits"))));
final ClientUpgradeRequest request = new ClientUpgradeRequest();
builder.configurator(new ClientEndpointConfig.Configurator() {
@Override
public void beforeRequest(Map<String, List<String>> headers) {
clientHeaders.forEach((key, value) -> {
if (SKIP_HEADERS.contains(key)) {
return;
}
request.addExtensions("permessage-deflate");
headers.remove(key);
headers.put(key, value);
});
clientHeaders.forEach((key, value) -> {
if (SKIP_HEADERS.contains(key)) {
return;
}
request.setHeader(key, value);
});
ClientEndpointConfig config = builder.build();
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);
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();
}
client.getHttpClient().setUserAgentField(clientUserAgent);
}
});
}
@Override
public void onClose(Session session, CloseReason closeReason) {
// Hotel closed connection.
client.shutdownProxy();
}
logger.info("Connecting to origin websocket at {}", websocketUrl);
@Override
public void onError(Session session, Throwable throwable) {
throwable.printStackTrace();
// Shutdown.
client.shutdownProxy();
client.start();
client.connect(this, URI.create(websocketUrl), request);
} catch (Exception e) {
throw new IOException("Failed to start websocket client to origin " + websocketUrl, e);
}
}
@Override
@ -118,10 +110,87 @@ public class NitroWebsocketServer extends Endpoint implements NitroSession {
try {
activeSession.close();
} catch (IOException e) {
} catch (Exception 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,6 +134,10 @@ 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,13 +6,17 @@ 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;
@ -31,6 +35,7 @@ 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;
}
@ -39,7 +44,13 @@ public class NitroPacketHandler extends PacketHandler {
buffer = buffer.clone();
}
localSession.getAsyncRemote().sendBinary(ByteBuffer.wrap(buffer));
try {
localSession.getRemote().sendBytes(ByteBuffer.wrap(buffer));
} catch (IOException e) {
logger.error("Error sending packet to nitro client", e);
return false;
}
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"));
viewIncoming.setKey(1, "ext.logger.state." + (chkViewOutgoing.isSelected() ? "true" : "false"));
viewOutgoing.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

@ -0,0 +1,105 @@
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,6 +8,7 @@ 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;
@ -18,6 +19,7 @@ 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;
@ -54,9 +56,10 @@ 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));
}));
extensionHandler.getObservable().addListener((e -> Platform.runLater(() -> {
producer.extensionConnected(e);
GEarthTrayIcon.addExtension(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 inforções de conexão inválidas, o G-Earth não conseguiu se conectar
alert.invalidconnection.content=Você entrou com informaçõ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=Exntesão "%s" tentou se conectar mas é desconcida pelo G-Earth,\n\
alert.extconnection.content=Extensão "%s" tentou se conectar mas é desconhecida 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=secondo
ext.store.elapsedtime.second.multiple=secondos
ext.store.elapsedtime.second.single=segundo
ext.store.elapsedtime.second.multiple=segundos
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:instalações adicionais-
ext.store.extension.warning.requirement=Aviso: o framework requer --url:additional installations-
! 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=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.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.ordering.rating=Rating
ext.store.ordering.alphabetical=Alphabetical
ext.store.ordering.lastupdated=Last updated
ext.store.ordering.newreleases=New releases
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
### 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=Menssagem
ext.logger.menu.packets.displaydetails.message=Mensagem
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,7 +36,6 @@ 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

View File

@ -1,24 +1,89 @@
**NOTE: Currently supported browsers: ONLY Firefox and Chromium, works on Habbo AIR too**
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.
# MacOS Installation Guide
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)
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.
Once created, we are able to codesign gmem from Terminal<br>
`codesign -fs "gmem-cert" <G-Earth_Path>/G-Mem`
There is a few steps u have to complete in order to get it to work on MacOS.
![](https://i.imgur.com/xkryoJz.png)
## Code-Sign G-Mem file and make it executable
Now you're ready to open G-Earth from Terminal<br>
`sudo java -jar G-Earth.jar`
### 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)
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)