-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
271 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
#!/usr/bin/env python3 | ||
# -*- coding: utf-8 -*- | ||
|
||
import time | ||
import socket | ||
import json | ||
import threading | ||
import uuid | ||
import os.path | ||
import base64 | ||
|
||
from mcdreforged.api.all import * | ||
from .byte_utils import * | ||
import online_player_api as lib_online_player | ||
|
||
|
||
# def on_load(server: PluginServerInterface, old_module): | ||
# server.logger.info("插件已加载") | ||
|
||
|
||
# 服务器启动事件 | ||
@new_thread | ||
def on_server_startup(server: PluginServerInterface): | ||
server.logger.info("事件:服务器启动") | ||
time.sleep(5) | ||
check_player_num(server) | ||
|
||
# 玩家加入事件 | ||
@new_thread | ||
def on_player_left(server: PluginServerInterface, player): | ||
server.logger.info("事件:玩家退出") | ||
time.sleep(5) | ||
check_player_num(server) | ||
|
||
# 倒数并关闭服务器 | ||
@spam_proof | ||
def check_player_num(server: PluginServerInterface): | ||
if len(lib_online_player.get_player_list()) == 0: | ||
|
||
check_config_fire(server) | ||
with open("config/HibernateR.json", "r") as file: | ||
config = json.load(file) | ||
wait_min = config["wait_min"] | ||
|
||
time.sleep(wait_min *60) | ||
if len(lib_online_player.get_player_list()) == 0: | ||
server.logger.info("倒计时结束,关闭服务器") | ||
server.stop() | ||
time.sleep(10) | ||
fake_server(server) | ||
else: | ||
server.logger.info("服务器内仍有玩家") | ||
else: return | ||
|
||
# FakeServer部分 | ||
@spam_proof | ||
def fake_server(server: PluginServerInterface): | ||
|
||
check_config_fire(server) | ||
with open("config/HibernateR.json", "r") as file: | ||
config = json.load(file) | ||
fs_ip = config["ip"] | ||
fs_port = config["port"] | ||
fs_samples = config["samples"] | ||
fs_motd = config["motd"]["1"] + "\n" + config["motd"]["2"] | ||
fs_icon = None | ||
fs_kick_message = "" | ||
|
||
for message in config["kick_message"]: | ||
fs_kick_message += message + "\n" | ||
|
||
if not os.path.exists(config["server_icon"]): | ||
server.logger.warning("未找到服务器图标,设置为None") | ||
else: | ||
with open(config["server_icon"], 'rb') as image: | ||
fs_icon = "data:image/png;base64," + base64.b64encode(image.read()).decode() | ||
|
||
while True: | ||
try: | ||
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) | ||
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) | ||
server_socket.bind((fs_ip, fs_port)) | ||
server_socket.settimeout(5000) | ||
except: | ||
server.logger.error("伪装服务端启动失败") | ||
server_socket.close() | ||
time.sleep(1) | ||
else: | ||
server.logger.info("伪装服务端已启动") | ||
break | ||
|
||
server_socket.listen(5) | ||
server.logger.info("开始监听端口") | ||
try: | ||
while True: | ||
server.logger.info("等待连接") | ||
client_socket, client_address = server_socket.accept() | ||
try: | ||
recv_data = client_socket.recv(1024) | ||
client_ip = client_address[0] | ||
(length, i) = read_varint(recv_data, 0) | ||
(packetID, i) = read_varint(recv_data, i) | ||
|
||
if packetID == 0: | ||
(version, i) = read_varint(recv_data, i) | ||
(ip, i) = read_utf(recv_data, i) | ||
|
||
ip = ip.replace('\x00', '').replace("\r", "\\r").replace("\t", "\\t").replace("\n", "\\n") | ||
is_using_fml = False | ||
if ip.endswith("FML"): | ||
is_using_fml = True | ||
ip = ip[:-3] | ||
(port, i) = read_ushort(recv_data, i) | ||
(state, i) = read_varint(recv_data, i) | ||
if state == 1: | ||
server.logger.info("伪装服务器收到了一次ping: %s" % (recv_data)) | ||
motd = {} | ||
motd["version"] = {} | ||
motd["version"]["name"] = "Sleeping" | ||
motd["version"]["protocol"] = 2 | ||
motd["players"] = {} | ||
motd["players"]["max"] = 10 | ||
motd["players"]["online"] = 10 | ||
motd["players"]["sample"] = [] | ||
|
||
for sample in fs_samples: | ||
motd["players"]["sample"].append( | ||
{"name": sample, "id": str(uuid.uuid4())}) | ||
|
||
motd["description"] = {"text": fs_motd} | ||
if fs_icon and len(fs_icon) > 0: | ||
motd["favicon"] = fs_icon | ||
write_response(client_socket, json.dumps(motd)) | ||
|
||
elif state == 2: | ||
server.logger.info("伪装服务器收到了一次连接请求: %s" % (recv_data)) | ||
|
||
# TM判断写一大堆,数据发过来一瞅总是少一截,离大谱。 | ||
# 找这个原因最起码花了我四五个小时 | ||
# 直接全部删掉,不是ping直接返回、 | ||
write_response(client_socket, json.dumps({"text": fs_kick_message})) | ||
start_server(server) | ||
return | ||
elif packetID == 1: | ||
(long, i) = read_long(recv_data, i) | ||
response = bytearray() | ||
write_varint(response, 9) | ||
write_varint(response, 1) | ||
bytearray.append(long) | ||
client_socket.sendall(bytearray) | ||
server.logger.info("Responded with pong packet.") | ||
else: | ||
server.logger.warning("收到了意外的数据包") | ||
|
||
except (TypeError, IndexError): # 错误处理(类型错误或索引错误) | ||
server.logger.warning("[%s:%s]收到了无效数据(%s)" % (client_ip, client_address[1], recv_data)) | ||
except Exception as e: | ||
server.logger.error(e) | ||
except: | ||
server.logger.info("关闭套接字") | ||
server_socket.close() | ||
|
||
# 启动服务器 | ||
def start_server(server: PluginServerInterface): | ||
server.logger.info("启动服务器") | ||
server.start() | ||
|
||
@new_thread | ||
def check_config_fire(server: PluginServerInterface): | ||
if os.path.exists("config/HibernateR.json"): | ||
pass | ||
else: | ||
server.logger.warning("未找到配置文件!正在以默认值创建") | ||
crative_config_fire() | ||
return | ||
|
||
def crative_config_fire(): | ||
config = {} | ||
config["wait_min"] = 10 | ||
config["ip"] = "0.0.0.0" | ||
config["port"] = 25565 | ||
config["protocol"] = 2 | ||
config["motd"] = {} | ||
config["motd"]["1"] = "§4Maintenance!" | ||
config["motd"]["2"] = "§aCheck example.com for more information!" | ||
config["version_text"] = "§4Maintenance" | ||
config["kick_message"] = ["§bSorry", "", "§aThis server is offline!"] | ||
config["server_icon"] = "server_icon.png" | ||
config["samples"] = ["§bexample.com", "", "§4Maintenance"] | ||
config["show_ip_if_hostname_available"] = True | ||
config["player_max"] = 0 | ||
config["player_online"] = 0 | ||
|
||
with open("config/HibernateR.json","w") as file: | ||
json.dump(config, file, sort_keys=True, indent=4, ensure_ascii=False) | ||
return |
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import struct | ||
|
||
|
||
def read_varint(byte, i): | ||
result = 0 | ||
bytes = 0 | ||
while True: | ||
byte_in = byte[i] | ||
i += 1 | ||
result |= (byte_in & 0x7F) << (bytes * 7) | ||
if bytes > 32: | ||
raise IOError("Packet is too long!") | ||
if (byte_in & 0x80) != 0x80: | ||
return result, i | ||
|
||
|
||
def read_utf(byte, i): | ||
(length, i) = read_varint(byte, i) | ||
ip = byte[i:(i + length)].decode('utf-8') | ||
i += length | ||
return ip, i | ||
|
||
|
||
def read_ushort(byte, i): | ||
new_i = i + 2 | ||
return struct.unpack(">H", byte[i:new_i])[0], new_i | ||
|
||
|
||
def read_long(byte, i): | ||
new_i = i + 8 | ||
return struct.unpack(">q", byte[i:new_i]), new_i | ||
|
||
|
||
def write_varint(byte, value): | ||
while True: | ||
part = value & 0x7F | ||
value >>= 7 | ||
if value != 0: | ||
part |= 0x80 | ||
byte.append(part) | ||
if value == 0: | ||
break | ||
|
||
|
||
def write_utf(byte, value): | ||
write_varint(byte, len(value)) | ||
for b in value.encode(): | ||
byte.append(b) | ||
|
||
def write_response(client_socket, response): | ||
response_array = bytearray() | ||
write_varint(response_array, 0) | ||
write_utf(response_array, response) | ||
length = bytearray() | ||
write_varint(length, len(response_array)) | ||
client_socket.sendall(length) | ||
client_socket.sendall(response_array) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
"id": "hibernate_r", | ||
"version": "0.1.0", | ||
"name": "HibernateR", | ||
"description": "一个MCDReforged插件,可以根据服务器内玩家情况自动开启或关闭服务器", | ||
"author": [ | ||
"HIM049", | ||
"sout_Nantang" | ||
], | ||
"link": "https://github.com/HIM049/MCDR_HibernateR", | ||
"dependencies": { | ||
"online_player_api": "*", | ||
"mcdreforged": ">=2.0.0-beta.3" | ||
}, | ||
"resources": [ | ||
"LICENSE" | ||
] | ||
} |