This commit is contained in:
8
src/main/java/moe/yuyui/mcstreamerbot/ClientProxy.java
Normal file
8
src/main/java/moe/yuyui/mcstreamerbot/ClientProxy.java
Normal file
@@ -0,0 +1,8 @@
|
||||
package moe.yuyui.mcstreamerbot;
|
||||
|
||||
public class ClientProxy extends CommonProxy {
|
||||
|
||||
// Override CommonProxy methods here, if you want a different behaviour on the client (e.g. registering renders).
|
||||
// Don't forget to call the super methods as well.
|
||||
|
||||
}
|
||||
35
src/main/java/moe/yuyui/mcstreamerbot/CommonProxy.java
Normal file
35
src/main/java/moe/yuyui/mcstreamerbot/CommonProxy.java
Normal file
@@ -0,0 +1,35 @@
|
||||
package moe.yuyui.mcstreamerbot;
|
||||
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLServerStartingEvent;
|
||||
import moe.yuyui.mcstreamerbot.events.minecraft.OnWorldJoin;
|
||||
import moe.yuyui.mcstreamerbot.events.minecraft.OnWorldLeave;
|
||||
|
||||
public class CommonProxy {
|
||||
|
||||
// preInit "Run before anything else. Read your config, create blocks, items, etc, and register them with the
|
||||
// GameRegistry." (Remove if not needed)
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
Config.synchronizeConfiguration(event.getSuggestedConfigurationFile());
|
||||
|
||||
MinecraftStreamerBotIntegration.LOG.info(Config.greeting);
|
||||
MinecraftStreamerBotIntegration.LOG.info("I am MyMod at version " + Tags.VERSION);
|
||||
}
|
||||
|
||||
// load "Do your mod setup. Build whatever data structures you care about. Register recipes." (Remove if not needed)
|
||||
public void init(FMLInitializationEvent event) {
|
||||
MinecraftForge.EVENT_BUS.register(new OnWorldJoin());
|
||||
MinecraftForge.EVENT_BUS.register(new OnWorldLeave());
|
||||
MinecraftStreamerBotIntegration.LOG.info("Loaded MCStreamerBot");
|
||||
}
|
||||
|
||||
// postInit "Handle interaction with other mods, complete your setup based on this." (Remove if not needed)
|
||||
public void postInit(FMLPostInitializationEvent event) {}
|
||||
|
||||
// register server commands in this event handler (Remove if not needed)
|
||||
public void serverStarting(FMLServerStartingEvent event) {}
|
||||
}
|
||||
20
src/main/java/moe/yuyui/mcstreamerbot/Config.java
Normal file
20
src/main/java/moe/yuyui/mcstreamerbot/Config.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package moe.yuyui.mcstreamerbot;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import net.minecraftforge.common.config.Configuration;
|
||||
|
||||
public class Config {
|
||||
|
||||
public static String greeting = "Hello World";
|
||||
|
||||
public static void synchronizeConfiguration(File configFile) {
|
||||
Configuration configuration = new Configuration(configFile);
|
||||
|
||||
greeting = configuration.getString("greeting", Configuration.CATEGORY_GENERAL, greeting, "How shall I greet?");
|
||||
|
||||
if (configuration.hasChanged()) {
|
||||
configuration.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package moe.yuyui.mcstreamerbot;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.SidedProxy;
|
||||
import cpw.mods.fml.common.event.FMLInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPostInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import cpw.mods.fml.common.event.FMLServerStartingEvent;
|
||||
import moe.yuyui.mcstreamerbot.common.EventListener;
|
||||
|
||||
@Mod(
|
||||
modid = MinecraftStreamerBotIntegration.MODID,
|
||||
version = Tags.VERSION,
|
||||
name = "MyMod",
|
||||
acceptedMinecraftVersions = "[1.7.10]")
|
||||
public class MinecraftStreamerBotIntegration {
|
||||
|
||||
public static final String MODID = "mcstreamerbot";
|
||||
public static final Logger LOG = LogManager.getLogger(MODID);
|
||||
|
||||
public static EventListener eventListener;
|
||||
|
||||
@SidedProxy(clientSide = "moe.yuyui.mcstreamerbot.ClientProxy", serverSide = "moe.yuyui.mcstreamerbot.CommonProxy")
|
||||
public static CommonProxy proxy;
|
||||
|
||||
@Mod.EventHandler
|
||||
// preInit "Run before anything else. Read your config, create blocks, items, etc, and register them with the
|
||||
// GameRegistry." (Remove if not needed)
|
||||
public void preInit(FMLPreInitializationEvent event) {
|
||||
proxy.preInit(event);
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
// load "Do your mod setup. Build whatever data structures you care about. Register recipes." (Remove if not needed)
|
||||
public void init(FMLInitializationEvent event) {
|
||||
proxy.init(event);
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
// postInit "Handle interaction with other mods, complete your setup based on this." (Remove if not needed)
|
||||
public void postInit(FMLPostInitializationEvent event) {
|
||||
proxy.postInit(event);
|
||||
}
|
||||
|
||||
@Mod.EventHandler
|
||||
// register server commands in this event handler (Remove if not needed)
|
||||
public void serverStarting(FMLServerStartingEvent event) {
|
||||
proxy.serverStarting(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package moe.yuyui.mcstreamerbot.common;
|
||||
|
||||
public class EventListener extends Thread {
|
||||
|
||||
private final WSServer _websocketServer;
|
||||
|
||||
public EventListener(WSServer websocketServer) {
|
||||
this._websocketServer = websocketServer;
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
this._websocketServer.run();
|
||||
System.out.println("Event Listener started");
|
||||
} catch (NullPointerException e) {
|
||||
System.out.println("Event Listener stopped. Websocket has failed.");
|
||||
}
|
||||
}
|
||||
|
||||
public void end() throws InterruptedException {
|
||||
this._websocketServer.getConnections()
|
||||
.forEach((connection) -> connection.closeConnection(1, "Server closed"));
|
||||
this._websocketServer.stop();
|
||||
}
|
||||
}
|
||||
138
src/main/java/moe/yuyui/mcstreamerbot/common/WSServer.java
Normal file
138
src/main/java/moe/yuyui/mcstreamerbot/common/WSServer.java
Normal file
@@ -0,0 +1,138 @@
|
||||
package moe.yuyui.mcstreamerbot.common;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.ZipException;
|
||||
|
||||
import org.java_websocket.WebSocket;
|
||||
import org.java_websocket.handshake.ClientHandshake;
|
||||
import org.java_websocket.server.WebSocketServer;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
|
||||
import moe.yuyui.mcstreamerbot.common.beans.MessagePayloadBean;
|
||||
import moe.yuyui.mcstreamerbot.common.enums.MessageType;
|
||||
import moe.yuyui.mcstreamerbot.events.custom.OnChatMessage;
|
||||
|
||||
public class WSServer extends WebSocketServer {
|
||||
|
||||
private final Set<WebSocket> _authClients = new HashSet<>();
|
||||
private byte[] _authToken = null;
|
||||
|
||||
public WSServer(InetSocketAddress address) {
|
||||
super(address);
|
||||
}
|
||||
|
||||
public WSServer(InetSocketAddress address, String authToken) throws NoSuchAlgorithmException {
|
||||
super(address);
|
||||
this._authToken = MessageDigest.getInstance("SHA-256")
|
||||
.digest(authToken.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(WebSocket conn, ClientHandshake handshake) {
|
||||
if (_authToken != null) {
|
||||
conn.send("bad girl");
|
||||
return;
|
||||
}
|
||||
_authClients.add(conn);
|
||||
System.out.println("New connection");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
|
||||
_authClients.remove(conn);
|
||||
System.out.println("Connection closed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket conn, String message) {}
|
||||
|
||||
private boolean isAuthenticated(WebSocket conn) {
|
||||
return _authClients.contains(conn);
|
||||
}
|
||||
|
||||
private boolean isBearerValid(String token) {
|
||||
if (_authToken == null) {
|
||||
return true;
|
||||
}
|
||||
if (token == null) {
|
||||
return false;
|
||||
}
|
||||
return token.equals(new String(_authToken, StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
public String decompressMessage(ByteBuffer byteBuffer) {
|
||||
if (byteBuffer == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(byteBuffer.array()));
|
||||
BufferedReader bufferedReader = new BufferedReader(
|
||||
new InputStreamReader(gzipInputStream, StandardCharsets.UTF_8));
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
builder.append(line);
|
||||
}
|
||||
return builder.toString();
|
||||
} catch (ZipException e) {
|
||||
System.out.println("Message is not valid");
|
||||
} catch (IOException ignored) {}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket conn, ByteBuffer byteBuffer) {
|
||||
String message = decompressMessage(byteBuffer);
|
||||
if (message == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Gson gson = new GsonBuilder().serializeNulls()
|
||||
.create();
|
||||
MessagePayloadBean payload = gson.fromJson(message, MessagePayloadBean.class);
|
||||
|
||||
if (!isAuthenticated(conn) || isBearerValid(payload.getToken())) {
|
||||
_authClients.add(conn);
|
||||
}
|
||||
switch (MessageType.valueOf(payload.getType())) {
|
||||
case CHAT: {
|
||||
OnChatMessage.sendMessage(payload.getParts(), payload.getTimestamp(), payload.getUser());
|
||||
break;
|
||||
}
|
||||
case COMMAND: {
|
||||
break;
|
||||
}
|
||||
case LISTEN: {
|
||||
break;
|
||||
}
|
||||
case IGNORE: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(WebSocket conn, Exception ex) {
|
||||
ex.printStackTrace();
|
||||
conn.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
System.out.println("Server started");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package moe.yuyui.mcstreamerbot.common.beans;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class EmojiBean {
|
||||
|
||||
@SerializedName("Emoji")
|
||||
private final String _emoji;
|
||||
@SerializedName("Text")
|
||||
private final String _text;
|
||||
@SerializedName("Image")
|
||||
private final String _imagePath;
|
||||
@SerializedName("StartIndex")
|
||||
private final int _startIndex;
|
||||
@SerializedName("EndIndex")
|
||||
private final int _endIndex;
|
||||
|
||||
public EmojiBean(String emoji, String text, String imagePath, int startIndex, int endIndex) {
|
||||
this._emoji = emoji;
|
||||
this._text = text;
|
||||
this._imagePath = imagePath;
|
||||
this._startIndex = startIndex;
|
||||
this._endIndex = endIndex;
|
||||
}
|
||||
|
||||
public String getEmoji() {
|
||||
return this._emoji;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this._text;
|
||||
}
|
||||
|
||||
public String getImagePath() {
|
||||
return this._imagePath;
|
||||
}
|
||||
|
||||
public int getStartIndex() {
|
||||
return this._startIndex;
|
||||
}
|
||||
|
||||
public int getEndIndex() {
|
||||
return this._endIndex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package moe.yuyui.mcstreamerbot.common.beans;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class MessageBean {
|
||||
|
||||
@SerializedName("Text")
|
||||
private final String _text;
|
||||
@SerializedName("Emoji")
|
||||
private final EmojiBean _emoji;
|
||||
|
||||
public MessageBean(String text, @Nullable EmojiBean emoji) {
|
||||
this._text = text;
|
||||
this._emoji = emoji;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this._text;
|
||||
}
|
||||
|
||||
public EmojiBean getEmoji() {
|
||||
return this._emoji;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package moe.yuyui.mcstreamerbot.common.beans;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class MessagePayloadBean {
|
||||
|
||||
@SerializedName("Type")
|
||||
private final short _type;
|
||||
@SerializedName("BearerToken")
|
||||
private final String _token;
|
||||
@SerializedName("User")
|
||||
private final UserBean _user;
|
||||
@SerializedName("Parts")
|
||||
private final List<MessageBean> _parts;
|
||||
@SerializedName("Timestamp")
|
||||
private final long _timestamp;
|
||||
|
||||
public MessagePayloadBean(short type, String token, UserBean user, List<MessageBean> parts, long timestamp) {
|
||||
this._type = type;
|
||||
this._token = token;
|
||||
this._user = user;
|
||||
this._parts = parts;
|
||||
this._timestamp = timestamp;
|
||||
}
|
||||
|
||||
public short getType() {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return this._token;
|
||||
}
|
||||
|
||||
public UserBean getUser() {
|
||||
return this._user;
|
||||
}
|
||||
|
||||
public List<MessageBean> getParts() {
|
||||
return this._parts;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return this._timestamp;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package moe.yuyui.mcstreamerbot.common.beans;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class UserBean {
|
||||
|
||||
@SerializedName("Name")
|
||||
private final String _name;
|
||||
@SerializedName("YoutubeId")
|
||||
private final String _youtubeId;
|
||||
@SerializedName("IsSubscriber")
|
||||
private final boolean _isSubscriber;
|
||||
@SerializedName("IsModerator")
|
||||
private final boolean _isModerator;
|
||||
@SerializedName("IsMember")
|
||||
private final boolean _isMember;
|
||||
|
||||
public UserBean(String name, String youtubeId, boolean isSubscriber, boolean isModerator, boolean isMember) {
|
||||
this._name = name;
|
||||
this._youtubeId = youtubeId;
|
||||
this._isSubscriber = isSubscriber;
|
||||
this._isModerator = isModerator;
|
||||
this._isMember = isMember;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return _name;
|
||||
}
|
||||
|
||||
public String getYoutubeId() {
|
||||
return _youtubeId;
|
||||
}
|
||||
|
||||
public boolean getIsSubscriber() {
|
||||
return _isSubscriber;
|
||||
}
|
||||
|
||||
public boolean getIsModerator() {
|
||||
return _isModerator;
|
||||
}
|
||||
|
||||
public boolean getIsMember() {
|
||||
return _isMember;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package moe.yuyui.mcstreamerbot.common.enums;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum MessageType {
|
||||
|
||||
CHAT(0),
|
||||
COMMAND(1),
|
||||
LISTEN(2),
|
||||
IGNORE(3);
|
||||
|
||||
private final int value;
|
||||
private static final Map<Integer, MessageType> map = new HashMap<>();
|
||||
|
||||
MessageType(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
static {
|
||||
for (MessageType messageType : MessageType.values()) {
|
||||
map.put(messageType.value, messageType);
|
||||
}
|
||||
}
|
||||
|
||||
public static MessageType valueOf(int value) {
|
||||
return (MessageType) map.get(value);
|
||||
}
|
||||
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package moe.yuyui.mcstreamerbot.events.custom;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.util.ChatComponentText;
|
||||
import net.minecraft.util.EnumChatFormatting;
|
||||
|
||||
import moe.yuyui.mcstreamerbot.common.beans.MessageBean;
|
||||
import moe.yuyui.mcstreamerbot.common.beans.UserBean;
|
||||
|
||||
public final class OnChatMessage {
|
||||
|
||||
private static final String _messageFormat = "<%s> %s%s[%s]%s: %s";
|
||||
|
||||
private static String buildMessage(List<MessageBean> messageParts) {
|
||||
StringBuilder finalMessage = new StringBuilder();
|
||||
for (MessageBean messagePart : messageParts) {
|
||||
if (messagePart.getEmoji() != null) {
|
||||
finalMessage.append(
|
||||
messagePart.getEmoji()
|
||||
.getText(),
|
||||
messagePart.getEmoji()
|
||||
.getStartIndex(),
|
||||
messagePart.getEmoji()
|
||||
.getEndIndex());
|
||||
}
|
||||
finalMessage.append(messagePart.getText());
|
||||
}
|
||||
return finalMessage.toString();
|
||||
}
|
||||
|
||||
private static String getUserColor(UserBean user) {
|
||||
if (user.getIsModerator()) {
|
||||
return EnumChatFormatting.RED.toString();
|
||||
}
|
||||
if (user.getIsMember()) {
|
||||
return EnumChatFormatting.DARK_GREEN.toString();
|
||||
}
|
||||
return EnumChatFormatting.RESET.toString();
|
||||
}
|
||||
|
||||
private static char getUserBadge(UserBean user) {
|
||||
if (user.getIsModerator()) {
|
||||
return '⚔';
|
||||
}
|
||||
if (user.getIsMember()) {
|
||||
return '✨';
|
||||
}
|
||||
return ' ';
|
||||
}
|
||||
|
||||
public static void sendMessage(List<MessageBean> messageParts, long timestamp, UserBean user) {
|
||||
if (messageParts == null) {
|
||||
return;
|
||||
}
|
||||
final String message = buildMessage(messageParts);
|
||||
final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("hh:mm:ss a");
|
||||
final String formattedTime = Instant.ofEpochSecond(timestamp)
|
||||
.atZone(ZoneId.systemDefault())
|
||||
.format(dateFormatter);
|
||||
ChatComponentText chatComponentText = new ChatComponentText(
|
||||
String.format(
|
||||
_messageFormat,
|
||||
formattedTime,
|
||||
getUserColor(user),
|
||||
getUserBadge(user),
|
||||
user.getName(),
|
||||
EnumChatFormatting.RESET.toString(),
|
||||
message.replace('§', ' ')));
|
||||
Minecraft.getMinecraft().ingameGUI.getChatGUI()
|
||||
.printChatMessage(chatComponentText);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package moe.yuyui.mcstreamerbot.events.minecraft;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraftforge.event.entity.EntityJoinWorldEvent;
|
||||
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import moe.yuyui.mcstreamerbot.MinecraftStreamerBotIntegration;
|
||||
import moe.yuyui.mcstreamerbot.common.EventListener;
|
||||
import moe.yuyui.mcstreamerbot.common.WSServer;
|
||||
|
||||
public class OnWorldJoin {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onEntityJoinWorld(EntityJoinWorldEvent event) {
|
||||
if (event.entity == Minecraft.getMinecraft().thePlayer && Minecraft.getMinecraft()
|
||||
.isIntegratedServerRunning() && event.world.isRemote) {
|
||||
if (MinecraftStreamerBotIntegration.eventListener != null) {
|
||||
if (MinecraftStreamerBotIntegration.eventListener.isAlive()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
MinecraftStreamerBotIntegration.eventListener = new EventListener(
|
||||
new WSServer(new InetSocketAddress("127.0.0.1", 8080)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package moe.yuyui.mcstreamerbot.events.minecraft;
|
||||
|
||||
import net.minecraftforge.event.world.WorldEvent;
|
||||
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import moe.yuyui.mcstreamerbot.MinecraftStreamerBotIntegration;
|
||||
|
||||
public class OnWorldLeave {
|
||||
|
||||
@SubscribeEvent
|
||||
public void onPlayerLeave(WorldEvent.Unload event) {
|
||||
try {
|
||||
MinecraftStreamerBotIntegration.eventListener.end();
|
||||
} catch (InterruptedException | NullPointerException ignored) {}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user