Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-27 08:30:12 +01:00
Merge branch 'master' into master
Dieser Commit ist enthalten in:
Commit
12b99c5b1b
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
38
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -7,30 +7,34 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--- Please follow this format COMPLETELY and make sure the bug you are reporting has not been reported yet. Reports should contain as much information or context as possible to help us find the problem. Simply creating an issue on a vague topic will not help us at all, and if you are unsure if something should belong here, please contact us on [Discord](http://discord.geysermc.org).-->
|
||||
|
||||
<!--- Issues pertaining to connection problem, or anything of that covered on the [Common Issues](https://github.com/GeyserMC/Geyser/wiki/Common-Issues) do not belong here and only clutter this issue tracker. -->
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
<!--- A clear and concise description of what the bug is. -->
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
<!--- Steps to reproduce the behavior: -->
|
||||
<!--- 1. Go to '...' -->
|
||||
<!--- 2. Click on '....' -->
|
||||
<!--- 3. Scroll down to '....' -->
|
||||
<!--- 4. See error -->
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
<!--- A clear and concise description of what you expected to happen. -->
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
**Screenshots / Videos**
|
||||
<!--- If applicable, add screenshots to help explain your problem. -->
|
||||
|
||||
**Server version**
|
||||
run /version
|
||||
**Server Version**
|
||||
<!--- Give us the exact output from /version. Saying "latest" does not help us at all. -->
|
||||
|
||||
**Geyser version**
|
||||
Jenkins
|
||||
**Geyser Version**
|
||||
<!--- Give us the exact build number as well as branch if applicable. Saying "latest" does not help us at all. -->
|
||||
|
||||
**Bedrock version**
|
||||
The version of your Minecraft pe
|
||||
**Minecraft: Bedrock Edition Version**
|
||||
<!-- The version of your Minecraft: Bedrock Edition client you tested with. -->
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
**Additional Context**
|
||||
<!--- Add any other context about the problem here. --->
|
||||
|
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normale Datei
5
.github/ISSUE_TEMPLATE/config.yml
vendored
Normale Datei
@ -0,0 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: GeyserMC Discord
|
||||
url: http://discord.geysermc.org/
|
||||
about: If your issue seems like it could possibly be an easy fix due to configuration, please hop on our Discord.
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -225,3 +225,4 @@ nbdist/
|
||||
config.yml
|
||||
logs/
|
||||
public-key.pem
|
||||
locales/
|
||||
|
@ -13,7 +13,7 @@ Geyser is a bridge between Minecraft: Bedrock Edition and Minecraft: Java Editio
|
||||
Geyser is a proxy, bridging the gap between Minecraft: Bedrock Edition and Minecraft: Java Edition servers.
|
||||
The ultimate goal of this project is to allow Minecraft: Bedrock Edition users to join Minecraft: Java Edition servers as seamlessly as possible. **Please note, this project is still a work in progress and should not be used on production. Expect bugs!**
|
||||
|
||||
### Currently supporting Minecraft Bedrock v1.14.X and Minecraft Java v1.15.2.
|
||||
### Currently supporting Minecraft Bedrock v1.14.6(0) and Minecraft Java v1.15.2.
|
||||
|
||||
## Setting Up
|
||||
Take a look [here](https://github.com/GeyserMC/Geyser/wiki#Setup) for how to set up Geyser.
|
||||
|
@ -54,7 +54,6 @@
|
||||
<shadedPattern>org.geysermc.platform.bukkit.shaded.fastutil</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -101,6 +101,11 @@ public class GeyserBukkitConfiguration implements IGeyserConfiguration {
|
||||
return config.getBoolean("allow-third-party-capes", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultLocale() {
|
||||
return config.getString("default-locale", "en_us");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFloodgateKeyFile() {
|
||||
return Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem"));
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -54,7 +54,6 @@
|
||||
<shadedPattern>org.geysermc.platform.bungeecord.shaded.netty</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -102,6 +102,11 @@ public class GeyserBungeeConfiguration implements IGeyserConfiguration {
|
||||
return config.getBoolean("allow-third-party-capes", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultLocale() {
|
||||
return config.getString("default-locale", "en_us");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFloodgateKeyFile() {
|
||||
return Paths.get(dataFolder.toString(), config.getString("floodgate-key-file", "public-key.pem"));
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -58,7 +58,6 @@
|
||||
<shadedPattern>org.geysermc.platform.sponge.shaded.fastutil</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -105,6 +105,11 @@ public class GeyserSpongeConfiguration implements IGeyserConfiguration {
|
||||
return node.getNode("allow-third-party-capes").getBoolean(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultLocale() {
|
||||
return node.getNode("default-locale").getString("en_us");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFloodgateKeyFile() {
|
||||
return Paths.get(dataFolder.toString(), node.getNode("floodgate-key-file").getString("public-key.pem"));
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -120,6 +120,9 @@
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>org.geysermc.platform.standalone.GeyserBootstrap</mainClass>
|
||||
<manifestEntries>
|
||||
<Multi-Release>true</Multi-Release>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
<transformer
|
||||
implementation="com.github.edwgiz.mavenShadePlugin.log4j2CacheTransformer.PluginsCacheFileTransformer">
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -63,6 +63,9 @@ public class GeyserConfiguration implements IGeyserConfiguration {
|
||||
@JsonProperty("allow-third-party-capes")
|
||||
private boolean allowThirdPartyCapes;
|
||||
|
||||
@JsonProperty("default-locale")
|
||||
private String defaultLocale;
|
||||
|
||||
private MetricsInfo metrics;
|
||||
|
||||
@Override
|
||||
|
@ -52,7 +52,7 @@ public class GeyserLogger extends SimpleTerminalConsole implements IGeyserLogger
|
||||
|
||||
@Override
|
||||
protected void shutdown() {
|
||||
GeyserConnector.getInstance().shutdown();
|
||||
GeyserConnector.getInstance().getBootstrap().onDisable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -44,7 +44,12 @@
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<minimizeJar>true</minimizeJar>
|
||||
<relocations>
|
||||
<relocation>
|
||||
<pattern>it.unimi.dsi.fastutil</pattern>
|
||||
<shadedPattern>org.geysermc.platform.velocity.shaded.fastutil</shadedPattern>
|
||||
</relocation>
|
||||
</relocations>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -63,6 +63,9 @@ public class GeyserVelocityConfiguration implements IGeyserConfiguration {
|
||||
@JsonProperty("allow-third-party-capes")
|
||||
private boolean allowThirdPartyCapes;
|
||||
|
||||
@JsonProperty("default-locale")
|
||||
private String defaultLocale;
|
||||
|
||||
private MetricsInfo metrics;
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -46,6 +46,8 @@ public interface IGeyserConfiguration {
|
||||
|
||||
boolean isAllowThirdPartyCapes();
|
||||
|
||||
String getDefaultLocale();
|
||||
|
||||
Path getFloodgateKeyFile();
|
||||
|
||||
IMetricsInfo getMetrics();
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@ -37,6 +37,10 @@ public class FormButton {
|
||||
@Getter
|
||||
private FormImage image;
|
||||
|
||||
public FormButton(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public FormButton(String text, FormImage image) {
|
||||
this.text = text;
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -30,16 +30,10 @@
|
||||
<version>2.9.8</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.sentry</groupId>
|
||||
<artifactId>sentry</artifactId>
|
||||
<version>1.7.0</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.nukkitx.protocol</groupId>
|
||||
<artifactId>bedrock-v389</artifactId>
|
||||
<version>2.5.4</version>
|
||||
<artifactId>bedrock-v390</artifactId>
|
||||
<version>2.5.5-SNAPSHOT</version>
|
||||
<scope>compile</scope>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
|
@ -27,7 +27,7 @@ package org.geysermc.connector;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
|
||||
import com.nukkitx.protocol.bedrock.BedrockServer;
|
||||
import com.nukkitx.protocol.bedrock.v389.Bedrock_v389;
|
||||
import com.nukkitx.protocol.bedrock.v390.Bedrock_v390;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@ -49,6 +49,8 @@ import java.net.InetSocketAddress;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -56,12 +58,12 @@ import java.util.concurrent.TimeUnit;
|
||||
@Getter
|
||||
public class GeyserConnector {
|
||||
|
||||
public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v389.V389_CODEC;
|
||||
public static final BedrockPacketCodec BEDROCK_PACKET_CODEC = Bedrock_v390.V390_CODEC;
|
||||
|
||||
public static final String NAME = "Geyser";
|
||||
public static final String VERSION = "1.0-SNAPSHOT";
|
||||
|
||||
private final Map<Object, GeyserSession> players = new HashMap<>();
|
||||
private final Map<InetSocketAddress, GeyserSession> players = new HashMap<>();
|
||||
|
||||
private static GeyserConnector instance;
|
||||
|
||||
@ -141,6 +143,40 @@ public class GeyserConnector {
|
||||
bootstrap.getGeyserLogger().info("Shutting down Geyser.");
|
||||
shuttingDown = true;
|
||||
|
||||
if (players.size() >= 1) {
|
||||
bootstrap.getGeyserLogger().info("Kicking " + players.size() + " player(s)");
|
||||
|
||||
for (GeyserSession playerSession : players.values()) {
|
||||
playerSession.disconnect("Geyser Proxy shutting down.");
|
||||
}
|
||||
|
||||
CompletableFuture<Void> future = CompletableFuture.runAsync(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Simulate a long-running Job
|
||||
try {
|
||||
while (true) {
|
||||
if (players.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
TimeUnit.MILLISECONDS.sleep(100);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Block and wait for the future to complete
|
||||
try {
|
||||
future.get();
|
||||
bootstrap.getGeyserLogger().info("Kicked all players");
|
||||
} catch (Exception e) {
|
||||
// Quietly fail
|
||||
}
|
||||
}
|
||||
|
||||
generalThreadPool.shutdown();
|
||||
bedrockServer.close();
|
||||
players.clear();
|
||||
@ -148,17 +184,15 @@ public class GeyserConnector {
|
||||
authType = null;
|
||||
commandMap.getCommands().clear();
|
||||
commandMap = null;
|
||||
|
||||
bootstrap.getGeyserLogger().info("Geyser shutdown successfully.");
|
||||
}
|
||||
|
||||
public void addPlayer(GeyserSession player) {
|
||||
players.put(player.getAuthData().getName(), player);
|
||||
players.put(player.getAuthData().getUUID(), player);
|
||||
players.put(player.getSocketAddress(), player);
|
||||
}
|
||||
|
||||
public void removePlayer(GeyserSession player) {
|
||||
players.remove(player.getAuthData().getName());
|
||||
players.remove(player.getAuthData().getUUID());
|
||||
players.remove(player.getSocketAddress());
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,11 @@ public class StopCommand extends GeyserCommand {
|
||||
if (!sender.isConsole() && connector.getPlatformType() == PlatformType.STANDALONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
connector.shutdown();
|
||||
|
||||
if (connector.getPlatformType() == PlatformType.STANDALONE) {
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class EnderCrystalEntity extends Entity {
|
||||
|
||||
public EnderCrystalEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
// Show beam
|
||||
// Usually performed client-side on Bedrock except for Ender Dragon respawn event
|
||||
if (entityMetadata.getId() == 7) {
|
||||
if (entityMetadata.getValue() instanceof Position) {
|
||||
Position pos = (Position) entityMetadata.getValue();
|
||||
metadata.put(EntityData.BLOCK_TARGET, Vector3i.from(pos.getX(), pos.getY(), pos.getZ()));
|
||||
} else {
|
||||
metadata.put(EntityData.BLOCK_TARGET, Vector3i.ZERO);
|
||||
}
|
||||
}
|
||||
// There is a base located on the ender crystal
|
||||
if (entityMetadata.getId() == 8) {
|
||||
metadata.getFlags().setFlag(EntityFlag.SHOW_BOTTOM, (boolean) entityMetadata.getValue());
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
AddEntityPacket addEntityPacket = new AddEntityPacket();
|
||||
// Not end crystal but ender crystal
|
||||
addEntityPacket.setIdentifier("minecraft:ender_crystal");
|
||||
addEntityPacket.setRuntimeEntityId(geyserId);
|
||||
addEntityPacket.setUniqueEntityId(geyserId);
|
||||
addEntityPacket.setPosition(position);
|
||||
addEntityPacket.setMotion(motion);
|
||||
addEntityPacket.setRotation(getBedrockRotation());
|
||||
addEntityPacket.setEntityType(entityType.getType());
|
||||
addEntityPacket.getMetadata().putAll(metadata);
|
||||
|
||||
valid = true;
|
||||
session.getUpstream().sendPacket(addEntityPacket);
|
||||
|
||||
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
|
||||
}
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.translators.block.BlockTranslator;
|
||||
|
||||
public class FallingBlockEntity extends Entity {
|
||||
|
||||
public FallingBlockEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation, int javaId) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
|
||||
this.metadata.put(EntityData.VARIANT, BlockTranslator.getBedrockBlockId(javaId));
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class FishingHookEntity extends Entity {
|
||||
public FishingHookEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
AddEntityPacket addEntityPacket = new AddEntityPacket();
|
||||
// Different ID in Bedrock
|
||||
addEntityPacket.setIdentifier("minecraft:fishing_hook");
|
||||
addEntityPacket.setRuntimeEntityId(geyserId);
|
||||
addEntityPacket.setUniqueEntityId(geyserId);
|
||||
addEntityPacket.setPosition(position);
|
||||
addEntityPacket.setMotion(motion);
|
||||
addEntityPacket.setRotation(getBedrockRotation());
|
||||
addEntityPacket.setEntityType(entityType.getType());
|
||||
addEntityPacket.getMetadata().putAll(metadata);
|
||||
|
||||
valid = true;
|
||||
session.getUpstream().sendPacket(addEntityPacket);
|
||||
|
||||
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
|
||||
}
|
||||
}
|
@ -26,8 +26,11 @@
|
||||
package org.geysermc.connector.entity;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.message.TextMessage;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.CommandPermission;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
@ -39,6 +42,8 @@ import lombok.Setter;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.scoreboard.Team;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
import org.geysermc.connector.utils.SkinUtils;
|
||||
|
||||
import java.util.UUID;
|
||||
@ -152,4 +157,27 @@ public class PlayerEntity extends LivingEntity {
|
||||
public void setPosition(Vector3f position) {
|
||||
this.position = position.add(0, entityType.getOffset(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
|
||||
if (entityMetadata.getId() == 2) {
|
||||
// System.out.println(session.getScoreboardCache().getScoreboard().getObjectives().keySet());
|
||||
for (Team team : session.getScoreboardCache().getScoreboard().getTeams().values()) {
|
||||
// session.getConnector().getLogger().info("team name " + team.getName());
|
||||
// session.getConnector().getLogger().info("team entities " + team.getEntities());
|
||||
}
|
||||
String username = this.username;
|
||||
TextMessage name = (TextMessage) entityMetadata.getValue();
|
||||
if (name != null) {
|
||||
username = MessageUtils.getBedrockMessage(name);
|
||||
}
|
||||
Team team = session.getScoreboardCache().getScoreboard().getTeamFor(username);
|
||||
if (team != null) {
|
||||
// session.getConnector().getLogger().info("team name es " + team.getName() + " with prefix " + team.getPrefix() + " and suffix " + team.getSuffix());
|
||||
metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,8 @@ public class BeeEntity extends AnimalEntity {
|
||||
if (entityMetadata.getId() == 16) {
|
||||
byte xd = (byte) entityMetadata.getValue();
|
||||
metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02);
|
||||
// If the bee has nectar or not
|
||||
metadata.getFlags().setFlag(EntityFlag.POWERED, (xd & 0x08) == 0x08);
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
@ -25,12 +25,52 @@
|
||||
|
||||
package org.geysermc.connector.entity.living.animal.horse;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.ItemData;
|
||||
import com.nukkitx.protocol.bedrock.packet.MobArmorEquipmentPacket;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.block.BlockTranslator;
|
||||
|
||||
public class LlamaEntity extends ChestedHorseEntity {
|
||||
|
||||
public LlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
// Strength
|
||||
if (entityMetadata.getId() == 19) {
|
||||
metadata.put(EntityData.STRENGTH, entityMetadata.getValue());
|
||||
}
|
||||
// Color equipped on the llama
|
||||
if (entityMetadata.getId() == 20) {
|
||||
// Bedrock treats llama decoration as armor
|
||||
MobArmorEquipmentPacket equipmentPacket = new MobArmorEquipmentPacket();
|
||||
equipmentPacket.setRuntimeEntityId(getGeyserId());
|
||||
// -1 means no armor
|
||||
if ((int) entityMetadata.getValue() != -1) {
|
||||
// The damage value is the dye color that Java sends us
|
||||
// Always going to be a carpet so we can hardcode 171 in BlockTranslator
|
||||
// The int then short conversion is required or we get a ClassCastException
|
||||
equipmentPacket.setChestplate(ItemData.of(BlockTranslator.CARPET, (short)((int) entityMetadata.getValue()), 1));
|
||||
} else {
|
||||
equipmentPacket.setChestplate(ItemData.AIR);
|
||||
}
|
||||
// Required to fill out the rest of the equipment or Bedrock ignores it, including above else statement if removing armor
|
||||
equipmentPacket.setBoots(ItemData.AIR);
|
||||
equipmentPacket.setHelmet(ItemData.AIR);
|
||||
equipmentPacket.setLeggings(ItemData.AIR);
|
||||
|
||||
session.getUpstream().sendPacket(equipmentPacket);
|
||||
}
|
||||
// Color of the llama
|
||||
if (entityMetadata.getId() == 21) {
|
||||
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity.living.animal.horse;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class TraderLlamaEntity extends LlamaEntity {
|
||||
|
||||
public TraderLlamaEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
// The trader llama is a separate entity from the llama in Java but a normal llama with extra metadata in Bedrock.
|
||||
AddEntityPacket addEntityPacket = new AddEntityPacket();
|
||||
addEntityPacket.setIdentifier("minecraft:llama");
|
||||
addEntityPacket.setRuntimeEntityId(geyserId);
|
||||
addEntityPacket.setUniqueEntityId(geyserId);
|
||||
addEntityPacket.setPosition(position);
|
||||
addEntityPacket.setMotion(motion);
|
||||
addEntityPacket.setRotation(getBedrockRotation());
|
||||
addEntityPacket.setEntityType(entityType.getType());
|
||||
addEntityPacket.getMetadata().putAll(metadata);
|
||||
// Here's the difference
|
||||
addEntityPacket.getMetadata().put(EntityData.MARK_VARIANT, 1);
|
||||
|
||||
valid = true;
|
||||
session.getUpstream().sendPacket(addEntityPacket);
|
||||
|
||||
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
|
||||
}
|
||||
|
||||
}
|
@ -28,6 +28,7 @@ package org.geysermc.connector.entity.living.animal.tameable;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityFlag;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
@ -40,12 +41,32 @@ public class CatEntity extends TameableEntity {
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
if (entityMetadata.getId() == 18) {
|
||||
metadata.put(EntityData.VARIANT, (int) entityMetadata.getValue());
|
||||
// Different colors in Java and Bedrock for some reason
|
||||
int variantColor;
|
||||
switch ((int) entityMetadata.getValue()) {
|
||||
case 0:
|
||||
variantColor = 8;
|
||||
break;
|
||||
case 8:
|
||||
variantColor = 0;
|
||||
break;
|
||||
case 9:
|
||||
variantColor = 10;
|
||||
break;
|
||||
case 10:
|
||||
variantColor = 9;
|
||||
break;
|
||||
default:
|
||||
variantColor = (int) entityMetadata.getValue();
|
||||
}
|
||||
metadata.put(EntityData.VARIANT, variantColor);
|
||||
}
|
||||
if (entityMetadata.getId() == 21) {
|
||||
// FIXME: Colors the whole animal instead of just collar
|
||||
// Needed or else wild cats are a red color
|
||||
if (metadata.getFlags().getFlag(EntityFlag.TAMED)) {
|
||||
metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
|
||||
}
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity.living.animal.tameable;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class ParrotEntity extends TameableEntity {
|
||||
|
||||
public ParrotEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
// Parrot color
|
||||
if (entityMetadata.getId() == 18) {
|
||||
metadata.put(EntityData.VARIANT, entityMetadata.getValue());
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
}
|
@ -27,6 +27,7 @@ package org.geysermc.connector.entity.living.animal.tameable;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityFlag;
|
||||
import org.geysermc.connector.entity.living.animal.AnimalEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
@ -45,6 +46,11 @@ public class TameableEntity extends AnimalEntity {
|
||||
metadata.getFlags().setFlag(EntityFlag.SITTING, (xd & 0x01) == 0x01);
|
||||
metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 0x02) == 0x02);
|
||||
metadata.getFlags().setFlag(EntityFlag.TAMED, (xd & 0x04) == 0x04);
|
||||
// Must be set for wolf collar color to work
|
||||
// Extending it to all entities to prevent future bugs
|
||||
if (metadata.getFlags().getFlag(EntityFlag.TAMED)) {
|
||||
metadata.put(EntityData.OWNER_EID, session.getPlayerEntity().getGeyserId());
|
||||
} // Can't de-tame an entity so no resetting the owner ID
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ package org.geysermc.connector.entity.living.animal.tameable;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityFlag;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
@ -39,9 +40,14 @@ public class WolfEntity extends TameableEntity {
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
// "Begging" on wiki.vg, "Interested" in Nukkit - the tilt of the head
|
||||
if (entityMetadata.getId() == 18) {
|
||||
metadata.getFlags().setFlag(EntityFlag.INTERESTED, (boolean) entityMetadata.getValue());
|
||||
}
|
||||
// Wolf collar color
|
||||
// Relies on EntityData.OWNER_EID being set in TameableEntity.java
|
||||
if (entityMetadata.getId() == 19) {
|
||||
// FIXME: Colors the whole animal instead of just collar
|
||||
// metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
|
||||
metadata.put(EntityData.COLOR, (byte) (int) entityMetadata.getValue());
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
@ -23,9 +23,10 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity.living;
|
||||
package org.geysermc.connector.entity.living.merchant;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import org.geysermc.connector.entity.living.AgeableEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
|
||||
public class AbstractMerchantEntity extends AgeableEntity {
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity.living.merchant;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class VillagerEntity extends AbstractMerchantEntity {
|
||||
|
||||
private static final Int2IntMap VILLAGER_VARIANTS = new Int2IntOpenHashMap();
|
||||
private static final Int2IntMap VILLAGER_REGIONS = new Int2IntOpenHashMap();
|
||||
|
||||
static {
|
||||
// Java villager profession IDs -> Bedrock
|
||||
VILLAGER_VARIANTS.put(0, 0);
|
||||
VILLAGER_VARIANTS.put(1, 8);
|
||||
VILLAGER_VARIANTS.put(2, 11);
|
||||
VILLAGER_VARIANTS.put(3, 6);
|
||||
VILLAGER_VARIANTS.put(4, 7);
|
||||
VILLAGER_VARIANTS.put(5, 1);
|
||||
VILLAGER_VARIANTS.put(6, 2);
|
||||
VILLAGER_VARIANTS.put(7, 4);
|
||||
VILLAGER_VARIANTS.put(8, 12);
|
||||
VILLAGER_VARIANTS.put(9, 5);
|
||||
VILLAGER_VARIANTS.put(10, 13);
|
||||
VILLAGER_VARIANTS.put(11, 14);
|
||||
VILLAGER_VARIANTS.put(12, 3);
|
||||
VILLAGER_VARIANTS.put(13, 10);
|
||||
VILLAGER_VARIANTS.put(14, 9);
|
||||
|
||||
VILLAGER_REGIONS.put(0, 1);
|
||||
VILLAGER_REGIONS.put(1, 2);
|
||||
VILLAGER_REGIONS.put(2, 0);
|
||||
VILLAGER_REGIONS.put(3, 3);
|
||||
VILLAGER_REGIONS.put(4, 4);
|
||||
VILLAGER_REGIONS.put(5, 5);
|
||||
VILLAGER_REGIONS.put(6, 6);
|
||||
}
|
||||
|
||||
public VillagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
if (entityMetadata.getId() == 17) {
|
||||
VillagerData villagerData = (VillagerData) entityMetadata.getValue();
|
||||
// Profession
|
||||
metadata.put(EntityData.VARIANT, VILLAGER_VARIANTS.get(villagerData.getProfession()));
|
||||
//metadata.put(EntityData.SKIN_ID, villagerData.getType()); Looks like this is modified but for any reason?
|
||||
// Region
|
||||
metadata.put(EntityData.MARK_VARIANT, VILLAGER_REGIONS.get(villagerData.getType()));
|
||||
// Trade tier - different indexing in Bedrock
|
||||
metadata.put(EntityData.TRADE_TIER, villagerData.getLevel() - 1);
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
AddEntityPacket addEntityPacket = new AddEntityPacket();
|
||||
// "v2" or else it's the legacy villager
|
||||
addEntityPacket.setIdentifier("minecraft:villager_v2");
|
||||
addEntityPacket.setRuntimeEntityId(geyserId);
|
||||
addEntityPacket.setUniqueEntityId(geyserId);
|
||||
addEntityPacket.setPosition(position);
|
||||
addEntityPacket.setMotion(motion);
|
||||
addEntityPacket.setRotation(getBedrockRotation());
|
||||
addEntityPacket.setEntityType(entityType.getType());
|
||||
addEntityPacket.getMetadata().putAll(metadata);
|
||||
|
||||
valid = true;
|
||||
session.getUpstream().sendPacket(addEntityPacket);
|
||||
|
||||
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity.living.monster;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.Attribute;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityEventType;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityFlag;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.EntityEventPacket;
|
||||
import org.geysermc.connector.entity.living.InsentientEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class EnderDragonEntity extends InsentientEntity {
|
||||
|
||||
public EnderDragonEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
if (entityMetadata.getId() == 15) {
|
||||
metadata.getFlags().setFlag(EntityFlag.FIRE_IMMUNE, true);
|
||||
switch ((int) entityMetadata.getValue()) {
|
||||
// Performing breath attack
|
||||
case 5:
|
||||
EntityEventPacket entityEventPacket = new EntityEventPacket();
|
||||
entityEventPacket.setType(EntityEventType.DRAGON_FLAMING);
|
||||
entityEventPacket.setRuntimeEntityId(geyserId);
|
||||
entityEventPacket.setData(0);
|
||||
session.getUpstream().sendPacket(entityEventPacket);
|
||||
case 6:
|
||||
case 7:
|
||||
metadata.getFlags().setFlag(EntityFlag.SITTING, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawnEntity(GeyserSession session) {
|
||||
AddEntityPacket addEntityPacket = new AddEntityPacket();
|
||||
addEntityPacket.setIdentifier("minecraft:" + entityType.name().toLowerCase());
|
||||
addEntityPacket.setRuntimeEntityId(geyserId);
|
||||
addEntityPacket.setUniqueEntityId(geyserId);
|
||||
addEntityPacket.setPosition(position);
|
||||
addEntityPacket.setMotion(motion);
|
||||
addEntityPacket.setRotation(getBedrockRotation());
|
||||
addEntityPacket.setEntityType(entityType.getType());
|
||||
addEntityPacket.getMetadata().putAll(metadata);
|
||||
|
||||
// Otherwise dragon is always 'dying'
|
||||
addEntityPacket.getAttributes().add(new Attribute("minecraft:health", 0.0f, 200f, 200f, 200f));
|
||||
|
||||
valid = true;
|
||||
session.getUpstream().sendPacket(addEntityPacket);
|
||||
|
||||
session.getConnector().getLogger().debug("Spawned entity " + entityType + " at location " + position + " with id " + geyserId + " (java id " + entityId + ")");
|
||||
}
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity.living.monster;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityFlag;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.block.BlockTranslator;
|
||||
|
||||
public class EndermanEntity extends MonsterEntity {
|
||||
|
||||
public EndermanEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
// Held block
|
||||
if (entityMetadata.getId() == 15) {
|
||||
metadata.put(EntityData.ENDERMAN_HELD_ITEM_ID, BlockTranslator.getBedrockBlockId((BlockState) entityMetadata.getValue()));
|
||||
}
|
||||
// 'Angry' - mouth open
|
||||
if (entityMetadata.getId() == 16) {
|
||||
metadata.getFlags().setFlag(EntityFlag.ANGRY, (boolean) entityMetadata.getValue());
|
||||
}
|
||||
// TODO: ID 17 is stared at but I don't believe it's used - maybe only for the sound effect. Check after particle merge
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.entity.living.monster;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import org.geysermc.connector.entity.living.GolemEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
|
||||
public class ShulkerEntity extends GolemEntity {
|
||||
|
||||
public ShulkerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) {
|
||||
super(entityId, geyserId, entityType, position, motion, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) {
|
||||
if (entityMetadata.getId() == 15) {
|
||||
BlockFace blockFace = (BlockFace) entityMetadata.getValue();
|
||||
metadata.put(EntityData.SHULKER_ATTACH_FACE, (byte) blockFace.ordinal());
|
||||
}
|
||||
if (entityMetadata.getId() == 16) {
|
||||
Position position = (Position) entityMetadata.getValue();
|
||||
if (position != null) {
|
||||
metadata.put(EntityData.SHULKER_ATTACH_POS, Vector3i.from(position.getX(), position.getY(), position.getZ()));
|
||||
}
|
||||
}
|
||||
//TODO Outdated metadata flag SHULKER_PEAK_HEIGHT
|
||||
// if (entityMetadata.getId() == 17) {
|
||||
// int height = (byte) entityMetadata.getValue();
|
||||
// metadata.put(EntityData.SHULKER_PEAK_HEIGHT, height);
|
||||
// }
|
||||
if (entityMetadata.getId() == 18) {
|
||||
int color = Math.abs((byte) entityMetadata.getValue() - 15);
|
||||
metadata.put(EntityData.VARIANT, color);
|
||||
}
|
||||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
}
|
||||
}
|
@ -29,13 +29,9 @@ import lombok.Getter;
|
||||
import org.geysermc.connector.entity.*;
|
||||
import org.geysermc.connector.entity.living.*;
|
||||
import org.geysermc.connector.entity.living.animal.*;
|
||||
import org.geysermc.connector.entity.living.animal.tameable.CatEntity;
|
||||
import org.geysermc.connector.entity.living.animal.tameable.TameableEntity;
|
||||
import org.geysermc.connector.entity.living.animal.horse.AbstractHorseEntity;
|
||||
import org.geysermc.connector.entity.living.animal.horse.ChestedHorseEntity;
|
||||
import org.geysermc.connector.entity.living.animal.horse.HorseEntity;
|
||||
import org.geysermc.connector.entity.living.animal.horse.LlamaEntity;
|
||||
import org.geysermc.connector.entity.living.animal.tameable.WolfEntity;
|
||||
import org.geysermc.connector.entity.living.animal.horse.*;
|
||||
import org.geysermc.connector.entity.living.animal.tameable.*;
|
||||
import org.geysermc.connector.entity.living.merchant.*;
|
||||
import org.geysermc.connector.entity.living.monster.*;
|
||||
import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity;
|
||||
import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity;
|
||||
@ -49,7 +45,7 @@ public enum EntityType {
|
||||
PIG(PigEntity.class, 12, 0.9f),
|
||||
SHEEP(SheepEntity.class, 13, 1.3f, 0.9f),
|
||||
WOLF(WolfEntity.class, 14, 0.85f, 0.6f),
|
||||
VILLAGER(AbstractMerchantEntity.class, 15, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
VILLAGER(VillagerEntity.class, 15, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
MOOSHROOM(AnimalEntity.class, 16, 1.4f, 0.9f),
|
||||
SQUID(WaterEntity.class, 17, 0.8f),
|
||||
RABBIT(RabbitEntity.class, 18, 0.5f, 0.4f),
|
||||
@ -64,8 +60,8 @@ public enum EntityType {
|
||||
ZOMBIE_HORSE(AbstractHorseEntity.class, 27, 1.6f, 1.3965f),
|
||||
POLAR_BEAR(PolarBearEntity.class, 28, 1.4f, 1.3f),
|
||||
LLAMA(LlamaEntity.class, 29, 1.87f, 0.9f),
|
||||
TRADER_LLAMA(LlamaEntity.class, 29, 1.187f, 0.9f),
|
||||
PARROT(TameableEntity.class, 30, 0.9f, 0.5f),
|
||||
TRADER_LLAMA(TraderLlamaEntity.class, 29, 1.187f, 0.9f),
|
||||
PARROT(ParrotEntity.class, 30, 0.9f, 0.5f),
|
||||
DOLPHIN(WaterEntity.class, 31, 0.6f, 0.9f),
|
||||
ZOMBIE(ZombieEntity.class, 32, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
CREEPER(CreeperEntity.class, 33, 1.7f, 0.6f, 0.6f, 1.62f),
|
||||
@ -73,7 +69,7 @@ public enum EntityType {
|
||||
SPIDER(SpiderEntity.class, 35, 0.9f, 1.4f, 1.4f, 1f),
|
||||
ZOMBIE_PIGMAN(MonsterEntity.class, 36, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
SLIME(InsentientEntity.class, 37, 0.51f),
|
||||
ENDERMAN(MonsterEntity.class, 38, 2.9f, 0.6f),
|
||||
ENDERMAN(EndermanEntity.class, 38, 2.9f, 0.6f),
|
||||
SILVERFISH(MonsterEntity.class, 39, 0.3f, 0.4f),
|
||||
CAVE_SPIDER(MonsterEntity.class, 40, 0.5f, 0.7f),
|
||||
GHAST(FlyingEntity.class, 41, 4.0f),
|
||||
@ -88,8 +84,8 @@ public enum EntityType {
|
||||
ELDER_GUARDIAN(GuardianEntity.class, 50, 1.9975f),
|
||||
NPC(PlayerEntity.class, 51, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
WITHER(MonsterEntity.class, 52, 3.5f, 0.9f),
|
||||
ENDER_DRAGON(InsentientEntity.class, 53, 4f, 13f),
|
||||
SHULKER(GolemEntity.class, 54, 1f, 1f),
|
||||
ENDER_DRAGON(EnderDragonEntity.class, 53, 4f, 13f),
|
||||
SHULKER(ShulkerEntity.class, 54, 1f, 1f),
|
||||
ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f),
|
||||
AGENT(Entity.class, 56, 0f),
|
||||
VINDICATOR(AbstractIllagerEntity.class, 57, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
@ -103,18 +99,18 @@ public enum EntityType {
|
||||
PLAYER(PlayerEntity.class, 63, 1.8f, 0.6f, 0.6f, 1.62f),
|
||||
ITEM(ItemEntity.class, 64, 0.25f, 0.25f),
|
||||
TNT(Entity.class, 65, 0.98f, 0.98f),
|
||||
FALLING_BLOCK(Entity.class, 66, 0.98f, 0.98f),
|
||||
FALLING_BLOCK(FallingBlockEntity.class, 66, 0.98f, 0.98f),
|
||||
MOVING_BLOCK(Entity.class, 67, 0f),
|
||||
EXPERIENCE_BOTTLE(ThrowableEntity.class, 68, 0.25f, 0.25f),
|
||||
EXPERIENCE_ORB(ExpOrbEntity.class, 69, 0f),
|
||||
EYE_OF_ENDER(Entity.class, 70, 0f),
|
||||
END_CRYSTAL(Entity.class, 71, 0f),
|
||||
END_CRYSTAL(EnderCrystalEntity.class, 71, 0f),
|
||||
FIREWORK_ROCKET(Entity.class, 72, 0f),
|
||||
TRIDENT(ArrowEntity.class, 73, 0f),
|
||||
TURTLE(AnimalEntity.class, 74, 0.4f, 1.2f),
|
||||
CAT(CatEntity.class, 75, 0.35f, 0.3f),
|
||||
SHULKER_BULLET(Entity.class, 76, 0f),
|
||||
FISHING_BOBBER(Entity.class, 77, 0f),
|
||||
FISHING_BOBBER(FishingHookEntity.class, 77, 0f),
|
||||
CHALKBOARD(Entity.class, 78, 0f),
|
||||
DRAGON_FIREBALL(ItemedFireballEntity.class, 79, 0f),
|
||||
ARROW(ArrowEntity.class, 80, 0.25f, 0.25f),
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -1,107 +0,0 @@
|
||||
package org.geysermc.connector.metrics;
|
||||
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.SentryClient;
|
||||
import io.sentry.SentryClientFactory;
|
||||
import io.sentry.context.Context;
|
||||
import io.sentry.event.BreadcrumbBuilder;
|
||||
import io.sentry.event.UserBuilder;
|
||||
|
||||
public class SentryMetrics {
|
||||
private static SentryClient sentry;
|
||||
|
||||
public static void init() {
|
||||
/*
|
||||
It is recommended that you use the DSN detection system, which
|
||||
will check the environment variable "SENTRY_DSN", the Java
|
||||
System Property "sentry.dsn", or the "sentry.properties" file
|
||||
in your classpath. This makes it easier to provide and adjust
|
||||
your DSN without needing to change your code. See the configuration
|
||||
page for more information.
|
||||
*/
|
||||
Sentry.init();
|
||||
|
||||
// You can also manually provide the DSN to the ``init`` method.
|
||||
Sentry.init();
|
||||
|
||||
/*
|
||||
It is possible to go around the static ``Sentry`` API, which means
|
||||
you are responsible for making the SentryClient instance available
|
||||
to your code.
|
||||
*/
|
||||
sentry = SentryClientFactory.sentryClient();
|
||||
|
||||
SentryMetrics metrics = new SentryMetrics();
|
||||
metrics.logWithStaticAPI();
|
||||
metrics.logWithInstanceAPI();
|
||||
}
|
||||
|
||||
/**
|
||||
* An example method that throws an exception.
|
||||
*/
|
||||
void unsafeMethod() {
|
||||
throw new UnsupportedOperationException("You shouldn't call this!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Examples using the (recommended) static API.
|
||||
*/
|
||||
void logWithStaticAPI() {
|
||||
// Note that all fields set on the context are optional. Context data is copied onto
|
||||
// all future events in the current context (until the context is cleared).
|
||||
|
||||
// Record a breadcrumb in the current context. By default the last 100 breadcrumbs are kept.
|
||||
Sentry.getContext().recordBreadcrumb(
|
||||
new BreadcrumbBuilder().setMessage("User made an action").build()
|
||||
);
|
||||
|
||||
// Set the user in the current context.
|
||||
Sentry.getContext().setUser(
|
||||
new UserBuilder().setEmail("hello@sentry.io").build()
|
||||
);
|
||||
|
||||
// Add extra data to future events in this context.
|
||||
Sentry.getContext().addExtra("extra", "thing");
|
||||
|
||||
// Add an additional tag to future events in this context.
|
||||
Sentry.getContext().addTag("tagName", "tagValue");
|
||||
|
||||
/*
|
||||
This sends a simple event to Sentry using the statically stored instance
|
||||
that was created in the ``main`` method.
|
||||
*/
|
||||
Sentry.capture("This is a test");
|
||||
|
||||
try {
|
||||
unsafeMethod();
|
||||
} catch (Exception e) {
|
||||
// This sends an exception event to Sentry using the statically stored instance
|
||||
// that was created in the ``main`` method.
|
||||
Sentry.capture(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Examples that use the SentryClient instance directly.
|
||||
*/
|
||||
void logWithInstanceAPI() {
|
||||
// Retrieve the current context.
|
||||
Context context = sentry.getContext();
|
||||
|
||||
// Record a breadcrumb in the current context. By default the last 100 breadcrumbs are kept.
|
||||
context.recordBreadcrumb(new BreadcrumbBuilder().setMessage("User made an action").build());
|
||||
|
||||
// Set the user in the current context.
|
||||
context.setUser(new UserBuilder().setEmail("geyser.project@gmail.com").build());
|
||||
|
||||
// This sends a simple event to Sentry.
|
||||
sentry.sendMessage("This is a test");
|
||||
|
||||
try {
|
||||
unsafeMethod();
|
||||
} catch (Exception e) {
|
||||
// This sends an exception event to Sentry.
|
||||
sentry.sendException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -80,6 +80,13 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler {
|
||||
pong.setMotd(config.getBedrock().getMotd1());
|
||||
pong.setMotd(config.getBedrock().getMotd2());
|
||||
}
|
||||
|
||||
//Bedrock will not even attempt a connection if the client thinks the server is full
|
||||
//so we have to fake it not being full
|
||||
if (pong.getPlayerCount() >= pong.getMaximumPlayerCount()) {
|
||||
pong.setMaximumPlayerCount(pong.getPlayerCount() + 1);
|
||||
}
|
||||
|
||||
return pong;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ package org.geysermc.connector.network;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import org.geysermc.common.AuthType;
|
||||
import org.geysermc.common.IGeyserConfiguration;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
@ -85,7 +86,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(ModalFormResponsePacket packet) {
|
||||
return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormData());
|
||||
return LoginEncryptionUtils.authenticateFromForm(session, connector, packet.getFormId(), packet.getFormData());
|
||||
}
|
||||
|
||||
private boolean couldLoginUserByName(String bedrockUsername) {
|
||||
@ -107,7 +108,7 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
||||
|
||||
@Override
|
||||
public boolean handle(MovePlayerPacket packet) {
|
||||
if (!session.isLoggedIn() && !session.isLoggingIn()) {
|
||||
if (!session.isLoggedIn() && !session.isLoggingIn() && session.getConnector().getAuthType() == AuthType.ONLINE) {
|
||||
// TODO it is safer to key authentication on something that won't change (UUID, not username)
|
||||
if (!couldLoginUserByName(session.getAuthData().getName())) {
|
||||
LoginEncryptionUtils.showLoginWindow(session);
|
||||
|
@ -26,6 +26,7 @@
|
||||
package org.geysermc.connector.network.session;
|
||||
|
||||
import com.github.steveice10.mc.auth.data.GameProfile;
|
||||
import com.github.steveice10.mc.auth.exception.request.InvalidCredentialsException;
|
||||
import com.github.steveice10.mc.auth.exception.request.RequestException;
|
||||
import com.github.steveice10.mc.protocol.MinecraftProtocol;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||
@ -38,6 +39,7 @@ import com.github.steveice10.packetlib.tcp.TcpSessionFactory;
|
||||
import com.nukkitx.math.GenericMath;
|
||||
import com.nukkitx.math.TrigMath;
|
||||
import com.nukkitx.math.vector.Vector2f;
|
||||
import com.nukkitx.math.vector.Vector2i;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.nbt.tag.CompoundTag;
|
||||
@ -63,6 +65,7 @@ import org.geysermc.connector.network.session.cache.*;
|
||||
import org.geysermc.connector.network.translators.Registry;
|
||||
import org.geysermc.connector.network.translators.block.BlockTranslator;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
import org.geysermc.connector.utils.Toolbox;
|
||||
import org.geysermc.floodgate.util.BedrockData;
|
||||
import org.geysermc.floodgate.util.EncryptionUtil;
|
||||
@ -96,6 +99,8 @@ public class GeyserSession implements CommandSender {
|
||||
|
||||
private DataCache<Packet> javaPacketCache;
|
||||
|
||||
@Setter
|
||||
private Vector2i lastChunkPosition = null;
|
||||
private int renderDistance;
|
||||
|
||||
private boolean loggedIn;
|
||||
@ -144,15 +149,6 @@ public class GeyserSession implements CommandSender {
|
||||
public void connect(RemoteServer remoteServer) {
|
||||
startGame();
|
||||
this.remoteServer = remoteServer;
|
||||
if (connector.getAuthType() != AuthType.ONLINE) {
|
||||
connector.getLogger().info(
|
||||
"Attempting to login using " + connector.getAuthType().name().toLowerCase() + " mode... " +
|
||||
(connector.getAuthType() == AuthType.OFFLINE ?
|
||||
"authentication is disabled." : "authentication will be encrypted"
|
||||
)
|
||||
);
|
||||
authenticate(authData.getName());
|
||||
}
|
||||
|
||||
ChunkUtils.sendEmptyChunks(this, playerEntity.getPosition().toInt(), 0, false);
|
||||
|
||||
@ -169,6 +165,18 @@ public class GeyserSession implements CommandSender {
|
||||
upstream.sendPacket(playStatusPacket);
|
||||
}
|
||||
|
||||
public void login() {
|
||||
if (connector.getAuthType() != AuthType.ONLINE) {
|
||||
connector.getLogger().info(
|
||||
"Attempting to login using " + connector.getAuthType().name().toLowerCase() + " mode... " +
|
||||
(connector.getAuthType() == AuthType.OFFLINE ?
|
||||
"authentication is disabled." : "authentication will be encrypted"
|
||||
)
|
||||
);
|
||||
authenticate(authData.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public void authenticate(String username) {
|
||||
authenticate(username, "");
|
||||
}
|
||||
@ -179,7 +187,7 @@ public class GeyserSession implements CommandSender {
|
||||
return;
|
||||
}
|
||||
|
||||
loggedIn = true;
|
||||
loggingIn = true;
|
||||
// new thread so clients don't timeout
|
||||
new Thread(() -> {
|
||||
try {
|
||||
@ -248,6 +256,17 @@ public class GeyserSession implements CommandSender {
|
||||
connector.getLogger().info(authData.getName() + " (logged in as: " + protocol.getProfile().getName() + ")" + " has connected to remote java server on address " + remoteServer.getAddress());
|
||||
playerEntity.setUuid(protocol.getProfile().getId());
|
||||
playerEntity.setUsername(protocol.getProfile().getName());
|
||||
|
||||
String locale = clientData.getLanguageCode();
|
||||
|
||||
// Let the user know there locale may take some time to download
|
||||
// as it has to be extracted from a JAR
|
||||
if (locale.toLowerCase().equals("en_us") && !LocaleUtils.LOCALE_MAPPINGS.containsKey("en_us")) {
|
||||
sendMessage("Downloading your locale (en_us) this may take some time");
|
||||
}
|
||||
|
||||
// Download and load the language for the player
|
||||
LocaleUtils.downloadAndLoadLocale(locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -255,6 +274,9 @@ public class GeyserSession implements CommandSender {
|
||||
loggingIn = false;
|
||||
loggedIn = false;
|
||||
connector.getLogger().info(authData.getName() + " has disconnected from remote java server on address " + remoteServer.getAddress() + " because of " + event.getReason());
|
||||
if (event.getCause() != null) {
|
||||
event.getCause().printStackTrace();
|
||||
}
|
||||
upstream.disconnect(event.getReason());
|
||||
}
|
||||
|
||||
@ -278,6 +300,9 @@ public class GeyserSession implements CommandSender {
|
||||
|
||||
downstream.getSession().connect();
|
||||
connector.addPlayer(this);
|
||||
} catch (InvalidCredentialsException | IllegalArgumentException e) {
|
||||
connector.getLogger().info("User '" + username + "' entered invalid login info, kicking.");
|
||||
disconnect("Invalid/incorrect login info");
|
||||
} catch (RequestException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
121
connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java
vendored
Normale Datei
121
connector/src/main/java/org/geysermc/connector/network/session/cache/BossBar.java
vendored
Normale Datei
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.session.cache;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.message.Message;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.BossEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket;
|
||||
import lombok.AllArgsConstructor;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class BossBar {
|
||||
|
||||
private GeyserSession session;
|
||||
|
||||
private long entityId;
|
||||
private Message title;
|
||||
private float health;
|
||||
private int color;
|
||||
private int overlay;
|
||||
private int darkenSky;
|
||||
|
||||
public void addBossBar() {
|
||||
addBossEntity();
|
||||
updateBossBar();
|
||||
}
|
||||
|
||||
public void updateBossBar() {
|
||||
BossEventPacket bossEventPacket = new BossEventPacket();
|
||||
bossEventPacket.setBossUniqueEntityId(entityId);
|
||||
bossEventPacket.setAction(BossEventPacket.Action.SHOW);
|
||||
bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getClientData().getLanguageCode()));
|
||||
bossEventPacket.setHealthPercentage(health);
|
||||
bossEventPacket.setColor(color); //ignored by client
|
||||
bossEventPacket.setOverlay(overlay);
|
||||
bossEventPacket.setDarkenSky(darkenSky);
|
||||
|
||||
session.getUpstream().sendPacket(bossEventPacket);
|
||||
}
|
||||
|
||||
public void updateTitle(Message title) {
|
||||
this.title = title;
|
||||
BossEventPacket bossEventPacket = new BossEventPacket();
|
||||
bossEventPacket.setBossUniqueEntityId(entityId);
|
||||
bossEventPacket.setAction(BossEventPacket.Action.TITLE);
|
||||
bossEventPacket.setTitle(MessageUtils.getTranslatedBedrockMessage(title, session.getClientData().getLanguageCode()));
|
||||
|
||||
session.getUpstream().sendPacket(bossEventPacket);
|
||||
}
|
||||
|
||||
public void updateHealth(float health) {
|
||||
this.health = health;
|
||||
BossEventPacket bossEventPacket = new BossEventPacket();
|
||||
bossEventPacket.setBossUniqueEntityId(entityId);
|
||||
bossEventPacket.setAction(BossEventPacket.Action.HEALTH_PERCENTAGE);
|
||||
bossEventPacket.setHealthPercentage(health);
|
||||
|
||||
session.getUpstream().sendPacket(bossEventPacket);
|
||||
}
|
||||
|
||||
public void removeBossBar() {
|
||||
BossEventPacket bossEventPacket = new BossEventPacket();
|
||||
bossEventPacket.setBossUniqueEntityId(entityId);
|
||||
bossEventPacket.setAction(BossEventPacket.Action.HIDE);
|
||||
|
||||
session.getUpstream().sendPacket(bossEventPacket);
|
||||
removeBossEntity();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bedrock still needs an entity to display the BossBar.<br>
|
||||
* Just like 1.8 but it doesn't care about which entity
|
||||
*/
|
||||
private void addBossEntity() {
|
||||
AddEntityPacket addEntityPacket = new AddEntityPacket();
|
||||
addEntityPacket.setUniqueEntityId(entityId);
|
||||
addEntityPacket.setRuntimeEntityId(entityId);
|
||||
addEntityPacket.setIdentifier("minecraft:creeper");
|
||||
addEntityPacket.setEntityType(33);
|
||||
addEntityPacket.setPosition(session.getPlayerEntity().getPosition());
|
||||
addEntityPacket.setRotation(Vector3f.ZERO);
|
||||
addEntityPacket.setMotion(Vector3f.ZERO);
|
||||
addEntityPacket.getMetadata().put(EntityData.SCALE, 0.01F); // scale = 0 doesn't work?
|
||||
|
||||
session.getUpstream().sendPacket(addEntityPacket);
|
||||
}
|
||||
|
||||
private void removeBossEntity() {
|
||||
RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket();
|
||||
removeEntityPacket.setUniqueEntityId(entityId);
|
||||
|
||||
session.getUpstream().sendPacket(removeEntityPacket);
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ public class EntityCache {
|
||||
private Long2ObjectMap<Entity> entities = Long2ObjectMaps.synchronize(new Long2ObjectOpenHashMap<>());
|
||||
private Long2LongMap entityIdTranslations = Long2LongMaps.synchronize(new Long2LongOpenHashMap());
|
||||
private Map<UUID, PlayerEntity> playerEntities = Collections.synchronizedMap(new HashMap<>());
|
||||
private Object2LongMap<UUID> bossbars = new Object2LongOpenHashMap<>();
|
||||
private Map<UUID, BossBar> bossBars = Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
@Getter
|
||||
private AtomicLong nextEntityId = new AtomicLong(2L);
|
||||
@ -116,24 +116,30 @@ public class EntityCache {
|
||||
playerEntities.remove(uuid);
|
||||
}
|
||||
|
||||
public long addBossBar(UUID uuid) {
|
||||
long entityId = getNextEntityId().incrementAndGet();
|
||||
bossbars.put(uuid, entityId);
|
||||
return entityId;
|
||||
public void addBossBar(UUID uuid, BossBar bossBar) {
|
||||
bossBars.put(uuid, bossBar);
|
||||
bossBar.addBossBar();
|
||||
}
|
||||
|
||||
public long getBossBar(UUID uuid) {
|
||||
return bossbars.containsKey(uuid) ? bossbars.get(uuid) : -1;
|
||||
public BossBar getBossBar(UUID uuid) {
|
||||
return bossBars.get(uuid);
|
||||
}
|
||||
|
||||
public long removeBossBar(UUID uuid) {
|
||||
return bossbars.remove(uuid);
|
||||
public void removeBossBar(UUID uuid) {
|
||||
BossBar bossBar = bossBars.remove(uuid);
|
||||
if (bossBar != null) {
|
||||
bossBar.removeBossBar();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateBossBars() {
|
||||
bossBars.values().forEach(BossBar::updateBossBar);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
entities = null;
|
||||
entityIdTranslations = null;
|
||||
playerEntities = null;
|
||||
bossbars = null;
|
||||
bossBars = null;
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ public class Registry<T> {
|
||||
if (MAP.containsKey(clazz)) {
|
||||
((PacketTranslator<P>) MAP.get(clazz)).translate(packet, session);
|
||||
return true;
|
||||
} else {
|
||||
GeyserConnector.getInstance().getLogger().debug("Could not find packet for " + (packet.toString().length() > 25 ? packet.getClass().getSimpleName() : packet));
|
||||
}
|
||||
} catch (Throwable ex) {
|
||||
GeyserConnector.getInstance().getLogger().error("Could not translate packet " + packet.getClass().getSimpleName(), ex);
|
||||
|
@ -33,12 +33,10 @@ import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerState;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerStatePacket;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
|
||||
@ -101,10 +99,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||
session.getDownstream().getSession().send(stopSleepingPacket);
|
||||
break;
|
||||
case BLOCK_INTERACT:
|
||||
ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket(position,
|
||||
BlockFace.values()[packet.getFace()],
|
||||
Hand.MAIN_HAND, 0, 0, 0, false);
|
||||
session.getDownstream().getSession().send(blockPacket);
|
||||
// Handled in BedrockInventoryTransactionTranslator
|
||||
break;
|
||||
case START_BREAK:
|
||||
ClientPlayerActionPacket startBreakingPacket = new ClientPlayerActionPacket(PlayerAction.START_DIGGING, new Position(packet.getBlockPosition().getX(),
|
||||
@ -128,6 +123,7 @@ public class BedrockActionTranslator extends PacketTranslator<PlayerActionPacket
|
||||
spawnPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
|
||||
session.getUpstream().sendPacket(spawnPacket);
|
||||
entity.updateBedrockAttributes(session);
|
||||
session.getEntityCache().updateBossBars();
|
||||
}
|
||||
break;
|
||||
case JUMP:
|
||||
|
@ -33,15 +33,26 @@ import com.github.steveice10.mc.protocol.data.game.entity.player.Hand;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerSwingArmPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.AnimatePacket;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Translator(packet = AnimatePacket.class)
|
||||
public class BedrockAnimateTranslator extends PacketTranslator<AnimatePacket> {
|
||||
|
||||
@Override
|
||||
public void translate(AnimatePacket packet, GeyserSession session) {
|
||||
// Stop the player sending animations before they have fully spawned into the server
|
||||
if (!session.isSpawned()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (packet.getAction()) {
|
||||
case SWING_ARM:
|
||||
ClientPlayerSwingArmPacket swingArmPacket = new ClientPlayerSwingArmPacket(Hand.MAIN_HAND);
|
||||
session.getDownstream().getSession().send(swingArmPacket);
|
||||
// Delay so entity damage can be processed first
|
||||
session.getConnector().getGeneralThreadPool().schedule(() ->
|
||||
session.getDownstream().getSession().send(new ClientPlayerSwingArmPacket(Hand.MAIN_HAND)),
|
||||
25,
|
||||
TimeUnit.MILLISECONDS
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.CommandRequestPacket;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
@Translator(packet = CommandRequestPacket.class)
|
||||
public class BedrockCommandRequestTranslator extends PacketTranslator<CommandRequestPacket> {
|
||||
@ -45,7 +46,13 @@ public class BedrockCommandRequestTranslator extends PacketTranslator<CommandReq
|
||||
if (session.getConnector().getPlatformType() == PlatformType.STANDALONE && command.startsWith("geyser ") && commandMap.getCommands().containsKey(command.split(" ")[1])) {
|
||||
commandMap.runCommand(session, command);
|
||||
} else {
|
||||
ClientChatPacket chatPacket = new ClientChatPacket(packet.getCommand());
|
||||
String message = packet.getCommand().trim();
|
||||
|
||||
if (MessageUtils.isTooLong(message, session)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClientChatPacket chatPacket = new ClientChatPacket(message);
|
||||
session.getDownstream().getSession().send(chatPacket);
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.bedrock;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerPlaceBlockPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
@ -49,19 +50,33 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||
public void translate(InventoryTransactionPacket packet, GeyserSession session) {
|
||||
switch (packet.getTransactionType()) {
|
||||
case ITEM_USE:
|
||||
if (packet.getActionType() == 1) {
|
||||
switch (packet.getActionType()) {
|
||||
case 0:
|
||||
ClientPlayerPlaceBlockPacket blockPacket = new ClientPlayerPlaceBlockPacket(
|
||||
new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ()),
|
||||
BlockFace.values()[packet.getFace()],
|
||||
Hand.MAIN_HAND,
|
||||
packet.getClickPosition().getX(), packet.getClickPosition().getY(), packet.getClickPosition().getZ(),
|
||||
false);
|
||||
session.getDownstream().getSession().send(blockPacket);
|
||||
break;
|
||||
case 1:
|
||||
ClientPlayerUseItemPacket useItemPacket = new ClientPlayerUseItemPacket(Hand.MAIN_HAND);
|
||||
session.getDownstream().getSession().send(useItemPacket);
|
||||
} else if (packet.getActionType() == 2) {
|
||||
break;
|
||||
case 2:
|
||||
PlayerAction action = session.getGameMode() == GameMode.CREATIVE ? PlayerAction.START_DIGGING : PlayerAction.FINISH_DIGGING;
|
||||
Position pos = new Position(packet.getBlockPosition().getX(), packet.getBlockPosition().getY(), packet.getBlockPosition().getZ());
|
||||
ClientPlayerActionPacket breakPacket = new ClientPlayerActionPacket(action, pos, BlockFace.values()[packet.getFace()]);
|
||||
session.getDownstream().getSession().send(breakPacket);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case ITEM_RELEASE:
|
||||
if (packet.getActionType() == 0) {
|
||||
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0, 0, 0), BlockFace.DOWN);
|
||||
// Followed to the Minecraft Protocol specification outlined at wiki.vg
|
||||
ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.RELEASE_USE_ITEM, new Position(0,0,0),
|
||||
BlockFace.DOWN);
|
||||
session.getDownstream().getSession().send(releaseItemPacket);
|
||||
}
|
||||
break;
|
||||
@ -70,11 +85,23 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator<Inve
|
||||
if (entity == null)
|
||||
return;
|
||||
|
||||
//https://wiki.vg/Protocol#Interact_Entity
|
||||
switch (packet.getActionType()) {
|
||||
case 0: //Interact
|
||||
Vector3f vector = packet.getClickPosition();
|
||||
ClientPlayerInteractEntityPacket entityPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
||||
InteractAction.values()[packet.getActionType()], vector.getX(), vector.getY(), vector.getZ(), Hand.MAIN_HAND);
|
||||
|
||||
session.getDownstream().getSession().send(entityPacket);
|
||||
ClientPlayerInteractEntityPacket interactPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
||||
InteractAction.INTERACT, Hand.MAIN_HAND);
|
||||
ClientPlayerInteractEntityPacket interactAtPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
||||
InteractAction.INTERACT_AT, vector.getX(), vector.getY(), vector.getZ(), Hand.MAIN_HAND);
|
||||
session.getDownstream().getSession().send(interactPacket);
|
||||
session.getDownstream().getSession().send(interactAtPacket);
|
||||
break;
|
||||
case 1: //Attack
|
||||
ClientPlayerInteractEntityPacket attackPacket = new ClientPlayerInteractEntityPacket((int) entity.getEntityId(),
|
||||
InteractAction.ATTACK);
|
||||
session.getDownstream().getSession().send(attackPacket);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -34,12 +34,13 @@ import org.geysermc.connector.utils.SkinUtils;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
|
||||
|
||||
@Translator(packet = SetLocalPlayerAsInitializedPacket.class)
|
||||
public class BedrockPlayerInitializedTranslator extends PacketTranslator<SetLocalPlayerAsInitializedPacket> {
|
||||
public class BedrockSetLocalPlayerAsInitializedTranslator extends PacketTranslator<SetLocalPlayerAsInitializedPacket> {
|
||||
@Override
|
||||
public void translate(SetLocalPlayerAsInitializedPacket packet, GeyserSession session) {
|
||||
if (session.getPlayerEntity().getGeyserId() == packet.getRuntimeEntityId()) {
|
||||
if (!session.getUpstream().isInitialized()) {
|
||||
session.getUpstream().setInitialized(true);
|
||||
session.login();
|
||||
|
||||
for (PlayerEntity entity : session.getEntityCache().getEntitiesByType(PlayerEntity.class)) {
|
||||
if (!entity.isValid()) {
|
@ -31,6 +31,7 @@ import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientChatPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.TextPacket;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
@Translator(packet = TextPacket.class)
|
||||
public class BedrockTextTranslator extends PacketTranslator<TextPacket> {
|
||||
@ -38,12 +39,24 @@ public class BedrockTextTranslator extends PacketTranslator<TextPacket> {
|
||||
@Override
|
||||
public void translate(TextPacket packet, GeyserSession session) {
|
||||
if (packet.getMessage().charAt(0) == '.') {
|
||||
ClientChatPacket chatPacket = new ClientChatPacket(packet.getMessage().replace(".", "/"));
|
||||
String message = packet.getMessage().replace(".", "/").trim();
|
||||
|
||||
if (MessageUtils.isTooLong(message, session)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClientChatPacket chatPacket = new ClientChatPacket(message);
|
||||
session.getDownstream().getSession().send(chatPacket);
|
||||
return;
|
||||
}
|
||||
|
||||
ClientChatPacket chatPacket = new ClientChatPacket(packet.getMessage());
|
||||
String message = packet.getMessage().trim();
|
||||
|
||||
if (MessageUtils.isTooLong(message, session)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClientChatPacket chatPacket = new ClientChatPacket(message);
|
||||
session.getDownstream().getSession().send(chatPacket);
|
||||
}
|
||||
}
|
||||
|
@ -55,6 +55,9 @@ public class BlockTranslator {
|
||||
private static final Int2ObjectMap<BlockState> BEDROCK_TO_JAVA_BLOCK_MAP = new Int2ObjectOpenHashMap<>();
|
||||
private static final IntSet WATERLOGGED = new IntOpenHashSet();
|
||||
|
||||
// Bedrock carpet ID, used in LlamaEntity.java for decoration
|
||||
public static final int CARPET = 171;
|
||||
|
||||
private static final int BLOCK_STATE_VERSION = 17760256;
|
||||
|
||||
static {
|
||||
@ -100,7 +103,8 @@ public class BlockTranslator {
|
||||
if ("minecraft:water[level=0]".equals(javaId)) {
|
||||
waterRuntimeId = bedrockRuntimeId;
|
||||
}
|
||||
boolean waterlogged = entry.getValue().has("waterlogged") && entry.getValue().get("waterlogged").booleanValue();
|
||||
boolean waterlogged = entry.getKey().contains("waterlogged=true")
|
||||
|| javaId.contains("minecraft:bubble_column") || javaId.contains("minecraft:kelp") || javaId.contains("seagrass");
|
||||
|
||||
if (waterlogged) {
|
||||
BEDROCK_TO_JAVA_BLOCK_MAP.putIfAbsent(bedrockRuntimeId | 1 << 31, new BlockState(javaRuntimeId));
|
||||
@ -177,6 +181,10 @@ public class BlockTranslator {
|
||||
return JAVA_TO_BEDROCK_BLOCK_MAP.get(state.getId());
|
||||
}
|
||||
|
||||
public static int getBedrockBlockId(int javaId) {
|
||||
return JAVA_TO_BEDROCK_BLOCK_MAP.get(javaId);
|
||||
}
|
||||
|
||||
public static BlockState getJavaBlockState(int bedrockId) {
|
||||
return BEDROCK_TO_JAVA_BLOCK_MAP.get(bedrockId);
|
||||
}
|
||||
|
@ -72,7 +72,18 @@ public class ItemTranslator {
|
||||
if (stack.getNbt() == null) {
|
||||
return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount());
|
||||
}
|
||||
return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount(), translateToBedrockNBT(stack.getNbt()));
|
||||
|
||||
// TODO: Create proper transformers instead of shoving everything here
|
||||
CompoundTag tag = stack.getNbt();
|
||||
IntTag mapId = tag.get("map");
|
||||
|
||||
if (mapId != null) {
|
||||
tag.put(new StringTag("map_uuid", mapId.getValue().toString()));
|
||||
tag.put(new IntTag("map_name_index", mapId.getValue()));
|
||||
}
|
||||
|
||||
|
||||
return ItemData.of(bedrockItem.getBedrockId(), (short) bedrockItem.getBedrockData(), stack.getAmount(), translateToBedrockNBT(tag));
|
||||
}
|
||||
|
||||
public ItemEntry getItem(ItemStack stack) {
|
||||
|
@ -26,80 +26,38 @@
|
||||
package org.geysermc.connector.network.translators.java;
|
||||
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.BossBar;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerBossBarPacket;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddEntityPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.BossEventPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.RemoveEntityPacket;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
@Translator(packet = ServerBossBarPacket.class)
|
||||
public class JavaBossBarTranslator extends PacketTranslator<ServerBossBarPacket> {
|
||||
@Override
|
||||
public void translate(ServerBossBarPacket packet, GeyserSession session) {
|
||||
BossEventPacket bossEventPacket = new BossEventPacket();
|
||||
bossEventPacket.setBossUniqueEntityId(session.getEntityCache().getBossBar(packet.getUuid()));
|
||||
|
||||
BossBar bossBar = session.getEntityCache().getBossBar(packet.getUuid());
|
||||
switch (packet.getAction()) {
|
||||
case ADD:
|
||||
long entityId = session.getEntityCache().addBossBar(packet.getUuid());
|
||||
addBossEntity(session, entityId);
|
||||
|
||||
bossEventPacket.setAction(BossEventPacket.Action.SHOW);
|
||||
bossEventPacket.setBossUniqueEntityId(entityId);
|
||||
bossEventPacket.setTitle(MessageUtils.getBedrockMessage(packet.getTitle()));
|
||||
bossEventPacket.setHealthPercentage(packet.getHealth());
|
||||
bossEventPacket.setColor(0); //ignored by client
|
||||
bossEventPacket.setOverlay(1);
|
||||
bossEventPacket.setDarkenSky(0);
|
||||
long entityId = session.getEntityCache().getNextEntityId().incrementAndGet();
|
||||
bossBar = new BossBar(session, entityId, packet.getTitle(), packet.getHealth(), 0, 1, 0);
|
||||
session.getEntityCache().addBossBar(packet.getUuid(), bossBar);
|
||||
break;
|
||||
case UPDATE_TITLE:
|
||||
bossEventPacket.setAction(BossEventPacket.Action.TITLE);
|
||||
bossEventPacket.setTitle(MessageUtils.getBedrockMessage(packet.getTitle()));
|
||||
if (bossBar != null) bossBar.updateTitle(packet.getTitle());
|
||||
break;
|
||||
case UPDATE_HEALTH:
|
||||
bossEventPacket.setAction(BossEventPacket.Action.HEALTH_PERCENTAGE);
|
||||
bossEventPacket.setHealthPercentage(packet.getHealth());
|
||||
if (bossBar != null) bossBar.updateHealth(packet.getHealth());
|
||||
break;
|
||||
case REMOVE:
|
||||
bossEventPacket.setAction(BossEventPacket.Action.HIDE);
|
||||
removeBossEntity(session, session.getEntityCache().removeBossBar(packet.getUuid()));
|
||||
session.getEntityCache().removeBossBar(packet.getUuid());
|
||||
break;
|
||||
case UPDATE_STYLE:
|
||||
case UPDATE_FLAGS:
|
||||
//todo
|
||||
return;
|
||||
}
|
||||
|
||||
session.getUpstream().sendPacket(bossEventPacket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bedrock still needs an entity to display the BossBar.<br>
|
||||
* Just like 1.8 but it doesn't care about which entity
|
||||
*/
|
||||
private void addBossEntity(GeyserSession session, long entityId) {
|
||||
AddEntityPacket addEntityPacket = new AddEntityPacket();
|
||||
addEntityPacket.setUniqueEntityId(entityId);
|
||||
addEntityPacket.setRuntimeEntityId(entityId);
|
||||
addEntityPacket.setIdentifier("minecraft:creeper");
|
||||
addEntityPacket.setEntityType(33);
|
||||
addEntityPacket.setPosition(session.getPlayerEntity().getPosition());
|
||||
addEntityPacket.setRotation(Vector3f.ZERO);
|
||||
addEntityPacket.setMotion(Vector3f.ZERO);
|
||||
addEntityPacket.getMetadata().put(EntityData.SCALE, 0.01F); // scale = 0 doesn't work?
|
||||
|
||||
session.getUpstream().sendPacket(addEntityPacket);
|
||||
}
|
||||
|
||||
private void removeBossEntity(GeyserSession session, long entityId) {
|
||||
RemoveEntityPacket removeEntityPacket = new RemoveEntityPacket();
|
||||
removeEntityPacket.setUniqueEntityId(entityId);
|
||||
|
||||
session.getUpstream().sendPacket(removeEntityPacket);
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,8 @@ import com.github.steveice10.mc.protocol.data.message.TranslationMessage;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerChatPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.TextPacket;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Translator(packet = ServerChatPacket.class)
|
||||
public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> {
|
||||
|
||||
@ -58,14 +60,20 @@ public class JavaChatTranslator extends PacketTranslator<ServerChatPacket> {
|
||||
break;
|
||||
}
|
||||
|
||||
String locale = session.getClientData().getLanguageCode();
|
||||
|
||||
if (packet.getMessage() instanceof TranslationMessage) {
|
||||
textPacket.setType(TextPacket.Type.TRANSLATION);
|
||||
textPacket.setNeedsTranslation(true);
|
||||
textPacket.setParameters(MessageUtils.getTranslationParams(((TranslationMessage) packet.getMessage()).getTranslationParams()));
|
||||
textPacket.setMessage(MessageUtils.getBedrockMessage(packet.getMessage()));
|
||||
|
||||
List<String> paramsTranslated = MessageUtils.getTranslationParams(((TranslationMessage) packet.getMessage()).getTranslationParams(), locale);
|
||||
textPacket.setParameters(paramsTranslated);
|
||||
|
||||
textPacket.setMessage(MessageUtils.insertParams(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, true), paramsTranslated));
|
||||
} else {
|
||||
textPacket.setNeedsTranslation(false);
|
||||
textPacket.setMessage(MessageUtils.getBedrockMessage(packet.getMessage()));
|
||||
|
||||
textPacket.setMessage(MessageUtils.getTranslatedBedrockMessage(packet.getMessage(), locale, false));
|
||||
}
|
||||
|
||||
session.getUpstream().sendPacket(textPacket);
|
||||
|
@ -29,7 +29,6 @@ import org.geysermc.connector.entity.PlayerEntity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerJoinGamePacket;
|
||||
@ -69,7 +68,6 @@ public class JavaJoinGameTranslator extends PacketTranslator<ServerJoinGamePacke
|
||||
session.setRenderDistance(packet.getViewDistance());
|
||||
|
||||
if (DimensionUtils.javaToBedrock(packet.getDimension()) != entity.getDimension()) {
|
||||
ChunkUtils.sendEmptyChunks(session, entity.getPosition().toInt(), 3, true);
|
||||
DimensionUtils.switchDimension(session, packet.getDimension());
|
||||
}
|
||||
}
|
||||
|
@ -33,14 +33,56 @@ import org.geysermc.connector.network.translators.Translator;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.ClientPluginMessagePacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerPluginMessagePacket;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@Translator(packet = ServerPluginMessagePacket.class)
|
||||
public class JavaPluginMessageTranslator extends PacketTranslator<ServerPluginMessagePacket> {
|
||||
|
||||
private static byte[] brandData;
|
||||
|
||||
static {
|
||||
byte[] data = GeyserConnector.NAME.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] varInt = writeVarInt(data.length);
|
||||
brandData = new byte[varInt.length + data.length];
|
||||
System.arraycopy(varInt, 0, brandData, 0, varInt.length);
|
||||
System.arraycopy(data, 0, brandData, varInt.length, data.length);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void translate(ServerPluginMessagePacket packet, GeyserSession session) {
|
||||
if (packet.getChannel().equals("minecraft:brand")) {
|
||||
session.getDownstream().getSession().send(
|
||||
new ClientPluginMessagePacket(packet.getChannel(), GeyserConnector.NAME.getBytes())
|
||||
new ClientPluginMessagePacket(packet.getChannel(), brandData)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] writeVarInt(int value) {
|
||||
byte[] data = new byte[getVarIntLength(value)];
|
||||
int index = 0;
|
||||
do {
|
||||
byte temp = (byte)(value & 0b01111111);
|
||||
value >>>= 7;
|
||||
if (value != 0) {
|
||||
temp |= 0b10000000;
|
||||
}
|
||||
data[index] = temp;
|
||||
index++;
|
||||
} while (value != 0);
|
||||
return data;
|
||||
}
|
||||
|
||||
private static int getVarIntLength(int number) {
|
||||
if ((number & 0xFFFFFF80) == 0) {
|
||||
return 1;
|
||||
} else if ((number & 0xFFFFC000) == 0) {
|
||||
return 2;
|
||||
} else if ((number & 0xFFE00000) == 0) {
|
||||
return 3;
|
||||
} else if ((number & 0xF0000000) == 0) {
|
||||
return 4;
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,9 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.java;
|
||||
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.LevelEventType;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelEventPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.attribute.AttributeType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
@ -35,6 +38,8 @@ import org.geysermc.connector.utils.DimensionUtils;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerRespawnPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetPlayerGameTypePacket;
|
||||
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
@Translator(packet = ServerRespawnPacket.class)
|
||||
public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket> {
|
||||
|
||||
@ -53,6 +58,12 @@ public class JavaRespawnTranslator extends PacketTranslator<ServerRespawnPacket>
|
||||
session.getUpstream().sendPacket(playerGameTypePacket);
|
||||
session.setGameMode(packet.getGamemode());
|
||||
|
||||
LevelEventPacket stopRainPacket = new LevelEventPacket();
|
||||
stopRainPacket.setType(LevelEventType.STOP_RAIN);
|
||||
stopRainPacket.setData(ThreadLocalRandom.current().nextInt(50000) + 10000);
|
||||
stopRainPacket.setPosition(Vector3f.ZERO);
|
||||
session.getUpstream().sendPacket(stopRainPacket);
|
||||
|
||||
if (entity.getDimension() != DimensionUtils.javaToBedrock(packet.getDimension())) {
|
||||
DimensionUtils.switchDimension(session, packet.getDimension());
|
||||
} else {
|
||||
|
@ -0,0 +1,278 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.java;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.command.CommandNode;
|
||||
import com.github.steveice10.mc.protocol.data.game.command.CommandParser;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.ServerDeclareCommandsPacket;
|
||||
import com.nukkitx.protocol.bedrock.data.CommandData;
|
||||
import com.nukkitx.protocol.bedrock.data.CommandEnumData;
|
||||
import com.nukkitx.protocol.bedrock.data.CommandParamData;
|
||||
import com.nukkitx.protocol.bedrock.packet.AvailableCommandsPacket;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Translator(packet = ServerDeclareCommandsPacket.class)
|
||||
public class JavaServerDeclareCommandsTranslator extends PacketTranslator<ServerDeclareCommandsPacket> {
|
||||
@Override
|
||||
public void translate(ServerDeclareCommandsPacket packet, GeyserSession session) {
|
||||
List<CommandData> commandData = new ArrayList<>();
|
||||
Map<Integer, String> commands = new HashMap<>();
|
||||
Map<Integer, List<CommandNode>> commandArgs = new HashMap<>();
|
||||
|
||||
// Get the first node, it should be a root node
|
||||
CommandNode rootNode = packet.getNodes()[packet.getFirstNodeIndex()];
|
||||
|
||||
// Loop through the root nodes to get all commands
|
||||
for (int nodeIndex : rootNode.getChildIndices()) {
|
||||
CommandNode node = packet.getNodes()[nodeIndex];
|
||||
|
||||
// Make sure we dont have duplicated commands (happens if there is more than 1 root node)
|
||||
if (commands.containsKey(nodeIndex)) { continue; }
|
||||
|
||||
// Get and update the commandArgs list with the found arguments
|
||||
if (node.getChildIndices().length >= 1) {
|
||||
for (int childIndex : node.getChildIndices()) {
|
||||
commandArgs.putIfAbsent(nodeIndex, new ArrayList<>());
|
||||
commandArgs.get(nodeIndex).add(packet.getNodes()[childIndex]);
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the command name into the list
|
||||
commands.put(nodeIndex, node.getName());
|
||||
}
|
||||
|
||||
// The command flags, not sure what these do apart from break things
|
||||
List<CommandData.Flag> flags = new ArrayList<>();
|
||||
|
||||
// Loop through all the found commands
|
||||
for (int commandID : commands.keySet()) {
|
||||
String commandName = commands.get(commandID);
|
||||
|
||||
// Create a basic alias
|
||||
CommandEnumData aliases = new CommandEnumData( commandName + "Aliases", new String[] { commandName.toLowerCase() }, false);
|
||||
|
||||
// Get and parse all params
|
||||
CommandParamData[][] params = getParams(commandID, packet.getNodes()[commandID], packet.getNodes());
|
||||
|
||||
// Build the completed command and add it to the final list
|
||||
CommandData data = new CommandData(commandName, "", flags, (byte) 0, aliases, params);
|
||||
commandData.add(data);
|
||||
}
|
||||
|
||||
// Add our commands to the AvailableCommandsPacket for the bedrock client
|
||||
AvailableCommandsPacket availableCommandsPacket = new AvailableCommandsPacket();
|
||||
for (CommandData data : commandData) {
|
||||
availableCommandsPacket.getCommands().add(data);
|
||||
}
|
||||
|
||||
GeyserConnector.getInstance().getLogger().debug("Sending command packet of " + commandData.size() + " commands");
|
||||
|
||||
// Finally, send the commands to the client
|
||||
session.getUpstream().sendPacket(availableCommandsPacket);
|
||||
}
|
||||
|
||||
private CommandParamData[][] getParams(int commandID, CommandNode commandNode, CommandNode[] allNodes) {
|
||||
// Check if the command is an alias and redirect it
|
||||
if (commandNode.getRedirectIndex() != -1) {
|
||||
GeyserConnector.getInstance().getLogger().debug("Redirecting command " + commandNode.getName() + " to " + allNodes[commandNode.getRedirectIndex()].getName());
|
||||
commandNode = allNodes[commandNode.getRedirectIndex()];
|
||||
}
|
||||
|
||||
if (commandNode.getChildIndices().length >= 1) {
|
||||
// Create the root param node and build all the children
|
||||
ParamInfo rootParam = new ParamInfo(commandNode, null);
|
||||
rootParam.buildChildren(allNodes);
|
||||
|
||||
List<CommandParamData[]> treeData = rootParam.getTree();
|
||||
CommandParamData[][] params = new CommandParamData[treeData.size()][];
|
||||
|
||||
// Fill the nested params array
|
||||
int i = 0;
|
||||
for (CommandParamData[] tree : treeData) {
|
||||
params[i] = tree;
|
||||
i++;
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
return new CommandParamData[0][0];
|
||||
}
|
||||
|
||||
private CommandParamData.Type mapCommandType(CommandParser parser) {
|
||||
if (parser == null) { return CommandParamData.Type.STRING; }
|
||||
|
||||
switch (parser) {
|
||||
case FLOAT:
|
||||
return CommandParamData.Type.FLOAT;
|
||||
|
||||
case INTEGER:
|
||||
return CommandParamData.Type.INT;
|
||||
|
||||
case ENTITY:
|
||||
case GAME_PROFILE:
|
||||
return CommandParamData.Type.TARGET;
|
||||
|
||||
case BLOCK_POS:
|
||||
return CommandParamData.Type.BLOCK_POSITION;
|
||||
|
||||
case COLUMN_POS:
|
||||
case VEC3:
|
||||
return CommandParamData.Type.POSITION;
|
||||
|
||||
case MESSAGE:
|
||||
return CommandParamData.Type.MESSAGE;
|
||||
|
||||
case NBT:
|
||||
case NBT_COMPOUND_TAG:
|
||||
case NBT_TAG:
|
||||
case NBT_PATH:
|
||||
return CommandParamData.Type.JSON;
|
||||
|
||||
case RESOURCE_LOCATION:
|
||||
return CommandParamData.Type.FILE_PATH;
|
||||
|
||||
case INT_RANGE:
|
||||
return CommandParamData.Type.INT_RANGE;
|
||||
|
||||
case BOOL:
|
||||
case DOUBLE:
|
||||
case STRING:
|
||||
case VEC2:
|
||||
case BLOCK_STATE:
|
||||
case BLOCK_PREDICATE:
|
||||
case ITEM_STACK:
|
||||
case ITEM_PREDICATE:
|
||||
case COLOR:
|
||||
case COMPONENT:
|
||||
case OBJECTIVE:
|
||||
case OBJECTIVE_CRITERIA:
|
||||
case OPERATION: // Possibly OPERATOR
|
||||
case PARTICLE:
|
||||
case ROTATION:
|
||||
case SCOREBOARD_SLOT:
|
||||
case SCORE_HOLDER:
|
||||
case SWIZZLE:
|
||||
case TEAM:
|
||||
case ITEM_SLOT:
|
||||
case MOB_EFFECT:
|
||||
case FUNCTION:
|
||||
case ENTITY_ANCHOR:
|
||||
case RANGE:
|
||||
case FLOAT_RANGE:
|
||||
case ITEM_ENCHANTMENT:
|
||||
case ENTITY_SUMMON:
|
||||
case DIMENSION:
|
||||
case TIME:
|
||||
default:
|
||||
return CommandParamData.Type.STRING;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter
|
||||
private class ParamInfo {
|
||||
private CommandNode paramNode;
|
||||
private CommandParamData paramData;
|
||||
private List<ParamInfo> children;
|
||||
|
||||
public ParamInfo(CommandNode paramNode, CommandParamData paramData) {
|
||||
this.paramNode = paramNode;
|
||||
this.paramData = paramData;
|
||||
this.children = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void buildChildren(CommandNode[] allNodes) {
|
||||
int enumIndex = -1;
|
||||
|
||||
for (int paramID : paramNode.getChildIndices()) {
|
||||
CommandNode paramNode = allNodes[paramID];
|
||||
|
||||
if (paramNode.getParser() == null) {
|
||||
if (enumIndex == -1) {
|
||||
enumIndex = children.size();
|
||||
|
||||
// Create the new enum command
|
||||
CommandEnumData enumData = new CommandEnumData(paramNode.getName(), new String[] { paramNode.getName() }, false);
|
||||
children.add(new ParamInfo(paramNode, new CommandParamData(paramNode.getName(), false, enumData, mapCommandType(paramNode.getParser()), null, Collections.emptyList())));
|
||||
} else {
|
||||
// Get the existing enum
|
||||
ParamInfo enumParamInfo = children.get(enumIndex);
|
||||
|
||||
// Extend the current list of enum values
|
||||
String[] enumOptions = Arrays.copyOf(enumParamInfo.getParamData().getEnumData().getValues(), enumParamInfo.getParamData().getEnumData().getValues().length + 1);
|
||||
enumOptions[enumOptions.length - 1] = paramNode.getName();
|
||||
|
||||
// Re-create the command using the updated values
|
||||
CommandEnumData enumData = new CommandEnumData(enumParamInfo.getParamData().getEnumData().getName(), enumOptions, false);
|
||||
children.set(enumIndex, new ParamInfo(enumParamInfo.getParamNode(), new CommandParamData(enumParamInfo.getParamData().getName(), false, enumData, enumParamInfo.getParamData().getType(), null, Collections.emptyList())));
|
||||
}
|
||||
}else{
|
||||
// Put the non-enum param into the list
|
||||
children.add(new ParamInfo(paramNode, new CommandParamData(paramNode.getName(), false, null, mapCommandType(paramNode.getParser()), null, Collections.emptyList())));
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively build all child options
|
||||
for (ParamInfo child : children) {
|
||||
child.buildChildren(allNodes);
|
||||
}
|
||||
}
|
||||
|
||||
public List<CommandParamData[]> getTree() {
|
||||
List<CommandParamData[]> treeParamData = new ArrayList<>();
|
||||
|
||||
for (ParamInfo child : children) {
|
||||
// Get the tree from the child
|
||||
List<CommandParamData[]> childTree = child.getTree();
|
||||
|
||||
// Un-pack the tree append the child node to it and push into the list
|
||||
for (CommandParamData[] subchild : childTree) {
|
||||
CommandParamData[] tmpTree = new ArrayList<CommandParamData>() {
|
||||
{
|
||||
add(child.getParamData());
|
||||
addAll(Arrays.asList(subchild));
|
||||
}
|
||||
}.toArray(new CommandParamData[0]);
|
||||
|
||||
treeParamData.add(tmpTree);
|
||||
}
|
||||
|
||||
// If we have no more child parameters just the child
|
||||
if (childTree.size() == 0) {
|
||||
treeParamData.add(new CommandParamData[] { child.getParamData() });
|
||||
}
|
||||
}
|
||||
|
||||
return treeParamData;
|
||||
}
|
||||
}
|
||||
}
|
@ -30,6 +30,7 @@ import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.client.world.ClientTeleportConfirmPacket;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.player.ServerPlayerPositionRotationPacket;
|
||||
@ -86,6 +87,8 @@ public class JavaPlayerPositionRotationTranslator extends PacketTranslator<Serve
|
||||
ClientTeleportConfirmPacket teleportConfirmPacket = new ClientTeleportConfirmPacket(packet.getTeleportId());
|
||||
session.getDownstream().getSession().send(teleportConfirmPacket);
|
||||
|
||||
ChunkUtils.updateChunkPosition(session, pos.toInt());
|
||||
|
||||
session.getConnector().getLogger().info("Spawned player at " + packet.getX() + " " + packet.getY() + " " + packet.getZ());
|
||||
return;
|
||||
}
|
||||
|
@ -28,7 +28,9 @@ package org.geysermc.connector.network.translators.java.entity.spawn;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.type.object.FallingBlockData;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.entity.FallingBlockEntity;
|
||||
import org.geysermc.connector.entity.type.EntityType;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
@ -59,12 +61,18 @@ public class JavaSpawnObjectTranslator extends PacketTranslator<ServerSpawnObjec
|
||||
|
||||
Class<? extends Entity> entityClass = type.getEntityClass();
|
||||
try {
|
||||
Entity entity;
|
||||
if (packet.getType() == ObjectType.FALLING_BLOCK) {
|
||||
entity = new FallingBlockEntity(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
type, position, motion, rotation, ((FallingBlockData) packet.getData()).getId());
|
||||
} else {
|
||||
Constructor<? extends Entity> entityConstructor = entityClass.getConstructor(long.class, long.class, EntityType.class,
|
||||
Vector3f.class, Vector3f.class, Vector3f.class);
|
||||
|
||||
Entity entity = entityConstructor.newInstance(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
entity = entityConstructor.newInstance(packet.getEntityId(), session.getEntityCache().getNextEntityId().incrementAndGet(),
|
||||
type, position, motion, rotation
|
||||
);
|
||||
}
|
||||
session.getEntityCache().spawnEntity(entity);
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException ex) {
|
||||
ex.printStackTrace();
|
||||
|
@ -52,12 +52,14 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||
case CREATE:
|
||||
scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
|
||||
.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setPrefix(MessageUtils.getBedrockMessage(packet.getPrefix()))
|
||||
.setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix()));
|
||||
break;
|
||||
case UPDATE:
|
||||
scoreboard.getTeam(packet.getTeamName())
|
||||
.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setPrefix(MessageUtils.getBedrockMessage(packet.getPrefix()))
|
||||
.setSuffix(MessageUtils.getBedrockMessage(packet.getSuffix()))
|
||||
.setUpdateType(UpdateType.UPDATE);
|
||||
|
@ -36,7 +36,6 @@ import org.geysermc.connector.world.chunk.ChunkSection;
|
||||
import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerChunkDataPacket;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.network.VarInts;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
|
||||
|
||||
@ -48,10 +47,16 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
|
||||
|
||||
@Override
|
||||
public void translate(ServerChunkDataPacket packet, GeyserSession session) {
|
||||
if (session.isSpawned()) {
|
||||
ChunkUtils.updateChunkPosition(session, session.getPlayerEntity().getPosition().toInt());
|
||||
}
|
||||
|
||||
if (packet.getColumn().getBiomeData() == null) //Non-full chunk
|
||||
return;
|
||||
|
||||
// Not sure if this is safe or not, however without this the client usually times out
|
||||
GeyserConnector.getInstance().getGeneralThreadPool().execute(() -> {
|
||||
try {
|
||||
if (packet.getColumn().getBiomeData() != null) { //Full chunk
|
||||
ChunkUtils.ChunkData chunkData = ChunkUtils.translateToBedrock(packet.getColumn());
|
||||
ByteBuf byteBuf = Unpooled.buffer(32);
|
||||
ChunkSection[] sections = chunkData.sections;
|
||||
@ -83,28 +88,6 @@ public class JavaChunkDataTranslator extends PacketTranslator<ServerChunkDataPac
|
||||
levelChunkPacket.setChunkZ(packet.getColumn().getZ());
|
||||
levelChunkPacket.setData(payload);
|
||||
session.getUpstream().sendPacket(levelChunkPacket);
|
||||
} else {
|
||||
final int xOffset = packet.getColumn().getX() << 4;
|
||||
final int zOffset = packet.getColumn().getZ() << 4;
|
||||
Chunk[] chunks = packet.getColumn().getChunks();
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
Chunk chunk = chunks[i];
|
||||
if (chunk == null) continue;
|
||||
final int yOffset = i * 16;
|
||||
for (int x = 0; x < 16; x++) {
|
||||
for (int y = 0; y < 16; y++) {
|
||||
for (int z = 0; z < 16; z++) {
|
||||
BlockState blockState = chunk.get(x, y, z);
|
||||
Vector3i pos = Vector3i.from(
|
||||
x + xOffset,
|
||||
y + yOffset,
|
||||
z + zOffset);
|
||||
ChunkUtils.updateBlock(session, blockState, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.java.world;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.entity.ServerEntityCollectItemPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.TakeItemEntityPacket;
|
||||
import org.geysermc.connector.entity.Entity;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
@Translator(packet = ServerEntityCollectItemPacket.class)
|
||||
public class JavaCollectItemTranslator extends PacketTranslator<ServerEntityCollectItemPacket> {
|
||||
|
||||
@Override
|
||||
public void translate(ServerEntityCollectItemPacket packet, GeyserSession session) {
|
||||
// This is the definition of translating - both packets take the same values
|
||||
TakeItemEntityPacket takeItemEntityPacket = new TakeItemEntityPacket();
|
||||
// Collected entity is the item
|
||||
Entity collectedEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectedEntityId());
|
||||
// Collector is the entity picking up the item
|
||||
Entity collectorEntity;
|
||||
if (packet.getCollectorEntityId() == session.getPlayerEntity().getEntityId()) {
|
||||
collectorEntity = session.getPlayerEntity();
|
||||
} else {
|
||||
collectorEntity = session.getEntityCache().getEntityByJavaId(packet.getCollectorEntityId());
|
||||
}
|
||||
takeItemEntityPacket.setRuntimeEntityId(collectorEntity.getGeyserId());
|
||||
takeItemEntityPacket.setItemRuntimeEntityId(collectedEntity.getGeyserId());
|
||||
session.getUpstream().sendPacket(takeItemEntityPacket);
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.network.translators.java.world;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.world.map.MapData;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerMapDataPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.ClientboundMapItemDataPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.MapColor;
|
||||
|
||||
@Translator(packet = ServerMapDataPacket.class)
|
||||
public class JavaMapDataTranslator extends PacketTranslator<ServerMapDataPacket> {
|
||||
@Override
|
||||
public void translate(ServerMapDataPacket packet, GeyserSession session) {
|
||||
ClientboundMapItemDataPacket mapItemDataPacket = new ClientboundMapItemDataPacket();
|
||||
|
||||
mapItemDataPacket.setUniqueMapId(packet.getMapId());
|
||||
mapItemDataPacket.setDimensionId(session.getPlayerEntity().getDimension());
|
||||
mapItemDataPacket.setLocked(packet.isLocked());
|
||||
mapItemDataPacket.setScale(packet.getScale());
|
||||
|
||||
MapData data = packet.getData();
|
||||
if (data != null) {
|
||||
mapItemDataPacket.setXOffset(data.getX());
|
||||
mapItemDataPacket.setYOffset(data.getY());
|
||||
mapItemDataPacket.setWidth(data.getColumns());
|
||||
mapItemDataPacket.setHeight(data.getRows());
|
||||
|
||||
// Every int entry is an ARGB color
|
||||
int[] colors = new int[data.getData().length];
|
||||
|
||||
int idx = 0;
|
||||
for (byte colorId : data.getData()) {
|
||||
colors[idx] = MapColor.fromId(colorId).toARGB();
|
||||
idx++;
|
||||
}
|
||||
|
||||
mapItemDataPacket.setColors(colors);
|
||||
}
|
||||
|
||||
session.getUpstream().getSession().sendPacket(mapItemDataPacket);
|
||||
}
|
||||
}
|
@ -25,6 +25,10 @@
|
||||
|
||||
package org.geysermc.connector.network.translators.java.world;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.data.GameRuleData;
|
||||
import com.nukkitx.protocol.bedrock.packet.GameRulesChangedPacket;
|
||||
import it.unimi.dsi.fastutil.longs.Long2LongMap;
|
||||
import it.unimi.dsi.fastutil.longs.Long2LongOpenHashMap;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
@ -35,11 +39,37 @@ import com.nukkitx.protocol.bedrock.packet.SetTimePacket;
|
||||
@Translator(packet = ServerUpdateTimePacket.class)
|
||||
public class JavaUpdateTimeTranslator extends PacketTranslator<ServerUpdateTimePacket> {
|
||||
|
||||
// If negative, the last time is stored so we know it's not some plugin behavior doing weird things.
|
||||
// Per-player for multi-world support
|
||||
private static final Long2LongMap LAST_RECORDED_TIMES = new Long2LongOpenHashMap();
|
||||
|
||||
@Override
|
||||
public void translate(ServerUpdateTimePacket packet, GeyserSession session) {
|
||||
|
||||
// Bedrock sends a GameRulesChangedPacket if there is no daylight cycle
|
||||
// Java just sends a negative long if there is no daylight cycle
|
||||
long lastTime = LAST_RECORDED_TIMES.getOrDefault(session.getPlayerEntity().getEntityId(), 0);
|
||||
long time = packet.getTime();
|
||||
|
||||
if (lastTime != time) {
|
||||
// https://minecraft.gamepedia.com/Day-night_cycle#24-hour_Minecraft_day
|
||||
SetTimePacket setTimePacket = new SetTimePacket();
|
||||
setTimePacket.setTime((int) Math.abs(packet.getTime()) % 24000);
|
||||
setTimePacket.setTime((int) Math.abs(time) % 24000);
|
||||
session.getUpstream().sendPacket(setTimePacket);
|
||||
// TODO: Performance efficient to always do this?
|
||||
LAST_RECORDED_TIMES.put(session.getPlayerEntity().getEntityId(), time);
|
||||
}
|
||||
if (lastTime < 0 && time >= 0) {
|
||||
setDoDaylightCycleGamerule(session, true);
|
||||
} else if (lastTime != time && time < 0) {
|
||||
setDoDaylightCycleGamerule(session, false);
|
||||
}
|
||||
}
|
||||
|
||||
private void setDoDaylightCycleGamerule(GeyserSession session, boolean doCycle) {
|
||||
GameRulesChangedPacket gameRulesChangedPacket = new GameRulesChangedPacket();
|
||||
gameRulesChangedPacket.getGameRules().add(new GameRuleData<>("dodaylightcycle", doCycle));
|
||||
session.getUpstream().sendPacket(gameRulesChangedPacket);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ package org.geysermc.connector.network.translators.java.world;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.utils.ChunkUtils;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.world.ServerUpdateViewPositionPacket;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
@ -38,9 +39,8 @@ public class JavaUpdateViewPositionTranslator extends PacketTranslator<ServerUpd
|
||||
|
||||
@Override
|
||||
public void translate(ServerUpdateViewPositionPacket packet, GeyserSession session) {
|
||||
NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket();
|
||||
chunkPublisherUpdatePacket.setPosition(Vector3i.from(packet.getChunkX() << 4, 0, packet.getChunkZ() << 4));
|
||||
chunkPublisherUpdatePacket.setRadius(session.getRenderDistance() << 4);
|
||||
session.getUpstream().sendPacket(chunkPublisherUpdatePacket);
|
||||
if (!session.isSpawned() && session.getLastChunkPosition() == null) {
|
||||
ChunkUtils.updateChunkPosition(session, Vector3i.from(packet.getChunkX() << 4, 64, packet.getChunkZ() << 4));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
package org.geysermc.connector.scoreboard;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
@ -44,10 +45,10 @@ public class Team {
|
||||
private UpdateType updateType = UpdateType.ADD;
|
||||
private String name;
|
||||
private String prefix;
|
||||
private TeamColor color;
|
||||
private String suffix;
|
||||
private Set<String> entities = new ObjectOpenHashSet<>();
|
||||
|
||||
|
||||
public Team(Scoreboard scoreboard, String id) {
|
||||
this.scoreboard = scoreboard;
|
||||
this.id = id;
|
||||
|
@ -29,8 +29,10 @@ import com.github.steveice10.mc.protocol.data.game.chunk.Chunk;
|
||||
import com.github.steveice10.mc.protocol.data.game.chunk.Column;
|
||||
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position;
|
||||
import com.github.steveice10.mc.protocol.data.game.world.block.BlockState;
|
||||
import com.nukkitx.math.vector.Vector2i;
|
||||
import com.nukkitx.math.vector.Vector3i;
|
||||
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.NetworkChunkPublisherUpdatePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateBlockPacket;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.Translators;
|
||||
@ -74,6 +76,20 @@ public class ChunkUtils {
|
||||
return chunkData;
|
||||
}
|
||||
|
||||
public static void updateChunkPosition(GeyserSession session, Vector3i position) {
|
||||
Vector2i chunkPos = session.getLastChunkPosition();
|
||||
Vector2i newChunkPos = Vector2i.from(position.getX() >> 4, position.getZ() >> 4);
|
||||
|
||||
if (chunkPos == null || !chunkPos.equals(newChunkPos)) {
|
||||
NetworkChunkPublisherUpdatePacket chunkPublisherUpdatePacket = new NetworkChunkPublisherUpdatePacket();
|
||||
chunkPublisherUpdatePacket.setPosition(position);
|
||||
chunkPublisherUpdatePacket.setRadius(session.getRenderDistance() << 4);
|
||||
session.getUpstream().sendPacket(chunkPublisherUpdatePacket);
|
||||
|
||||
session.setLastChunkPosition(newChunkPos);
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateBlock(GeyserSession session, BlockState blockState, Position position) {
|
||||
Vector3i pos = Vector3i.from(position.getX(), position.getY(), position.getZ());
|
||||
updateBlock(session, blockState, pos);
|
||||
|
@ -52,6 +52,7 @@ public class DimensionUtils {
|
||||
player.setDimension(bedrockDimension);
|
||||
player.setPosition(pos.toFloat());
|
||||
session.setSpawned(false);
|
||||
session.setLastChunkPosition(null);
|
||||
|
||||
//let java server handle portal travel sound
|
||||
StopSoundPacket stopSoundPacket = new StopSoundPacket();
|
||||
|
@ -65,4 +65,23 @@ public class FileUtils {
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
public static void writeFile(File file, char[] data) throws IOException {
|
||||
if (!file.exists()) {
|
||||
file.createNewFile();
|
||||
}
|
||||
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
|
||||
for (char c : data) {
|
||||
fos.write(c);
|
||||
}
|
||||
|
||||
fos.flush();
|
||||
fos.close();
|
||||
}
|
||||
|
||||
public static void writeFile(String name, char[] data) throws IOException {
|
||||
writeFile(new File(name), data);
|
||||
}
|
||||
}
|
||||
|
Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden Mehr anzeigen
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren