Sending too much packets downstream causes the client to "Connection Lost" without any error message on the server.
Hello,
after setting up a bridge and playing with maps (the item) and their content (the data displayed), I noticed that my client is "kicked" (Connection Lost) when too much packets are sent downstream too quickly.
There are no error messages either in the client's game output or in the bridge's logs.
I know that I may ask too much from the client or the bridge but I am wondering :
- What is causing this issue ? Is it client side or bridge side ? I am unsure as there are no errors logged anywhere.
- Is there a way to avoid being kicked without having to send less packets ?
How to reproduce the issue:
- Create a new
.pyfile with the following code:
from twisted.internet import reactor
from quarry.net.proxy import DownstreamFactory, Bridge
import random
from threading import Thread
import time
WHITE = 34
BLACK = 119
class CustomBridge(Bridge):
animate = False
def packet_upstream_chat_message(self, buff):
buff.save()
chat_message = self.read_chat(buff, "upstream")
if chat_message.startswith("/anim"):
Thread(target=self.test_animate_map).start()
else:
buff.restore()
self.upstream.send_packet("chat_message", buff.read())
def test_animate_map(self):
if self.animate:
return
self.animate = True
print('Starting animation...')
for _ in range(100):
# https://wiki.vg/Protocol#Map_Data
map_id = 1
scale = 0
locked = False
tracking_pos = False
columns = 128
rows = 128
x, y = 0, 0
length = 16384
data = self.buff_type.pack_varint(map_id)
data += self.buff_type.pack('B??', scale, locked, tracking_pos)
data += self.buff_type.pack('B', columns)
data += self.buff_type.pack('BBB', rows, x, y)
data += self.buff_type.pack_varint(length)
# random noise
mapdata = (random.choice([WHITE, BLACK]) for _ in range(length))
data += self.buff_type.pack(f'{length}B', *mapdata)
self.downstream.send_packet('map', data)
time.sleep(0.10)
self.animate = False
print('Ended animation.')
# from proxy_hide_chat.py
def read_chat(self, buff, direction):
buff.save()
if direction == "upstream":
p_text = buff.unpack_string()
return p_text
elif direction == "downstream":
p_text = str(buff.unpack_chat())
p_position = 0
# 1.8.x+
if self.upstream.protocol_version >= 47:
p_position = buff.unpack('B')
if p_position in (0, 1):
return p_text
class QuietDownstreamFactory(DownstreamFactory):
bridge_class = CustomBridge
motd = "Custom Proxy Server"
def main(argv):
# Parse options
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--listen-host", default="", help="address to listen on")
parser.add_argument("-p", "--listen-port", default=25565, type=int, help="port to listen on")
parser.add_argument("-b", "--connect-host", default="127.0.0.1", help="address to connect to")
parser.add_argument("-q", "--connect-port", default=25565, type=int, help="port to connect to")
args = parser.parse_args(argv)
# Create factory
factory = QuietDownstreamFactory()
factory.connect_host = args.connect_host
factory.connect_port = args.connect_port
# Listen
factory.listen(args.listen_host, args.listen_port)
reactor.run()
if __name__ == "__main__":
import sys
main(sys.argv[1:])
- Run the script against an already running server (
python scriptname.py -b SERVER_IP) - Connect to the bridge you just opened
- Obtain the map that has the ID
#1(or change the map_id in the script) and hold it in your hand for example - Type
/animin the chat - Watch the map changing rapidly, and be eventually kicked due to
Connection Lost. - (eventually change the value of
time.sleepif you want to be kicked faster)
IT IS NOT THE AMOUNT OF PACKETS
You cannot time.sleep in reactor, It will kick you because you are not sending or receiving packets for a extended time, This would not work.
BELOW IS FIRST ANSWER, but I just desided to write a fixed ver (lol)
You can try to use lib https://twistedmatrix.com/documents/9.0.0/api/twisted.internet.task.LoopingCall.html
from twisted.internet.task import LoopingCallcode:
if not("lc" in self.data[self.downstream.uuid]): self.data[self.downstream.uuid]["lc"] = LoopingCall(self.loop) self.data[self.downstream.uuid]["lc"].start(0.3)with the variable of time set to 0 on start, (looping call has a api for count IM BAD)
from quarry.net.proxy import DownstreamFactory, Bridge
from twisted.internet.task import LoopingCall
import struct as struct
import random
from threading import Thread
import time
WHITE = 34
BLACK = 119
class CustomBridge(Bridge):
data = {}
def packet_downstream_join_game(self,buff):
self.start()
buff.save()
self.data[self.downstream.uuid]["ent_id"] = struct.unpack(">i",buff.read()[0:4])[0]
buff.restore()
print("NAME:"+self.downstream.display_name+" ENT ID:"+str(self.data[self.downstream.uuid]["ent_id"])+" Joined the game with uuid:"+str(self.downstream.uuid))
self.downstream.send_packet("join_game",buff.read())
def start(self):
self.data[self.downstream.uuid] = {}
self.data[self.downstream.uuid]["frame"] = 0
self.data[self.downstream.uuid]["animate"] = False
def packet_upstream_chat_message(self, buff):
buff.save()
chat_message = self.read_chat(buff, "upstream")
if chat_message.startswith("/anim"):
self.test_animate_map()
else:
buff.restore()
self.upstream.send_packet("chat_message", buff.read())
def ani_frame(self):
# https://wiki.vg/Protocol#Map_Data
#starts at 0 not 1
map_id = 1
scale = 0
#locked was false should be true
locked = True
tracking_pos = False
columns = 128
rows = 128
x, y = 0, 0
length = 16384
data = self.buff_type.pack_varint(map_id)
data += self.buff_type.pack('B??', scale, locked, tracking_pos)
data += self.buff_type.pack('B', columns)
data += self.buff_type.pack('BBB', rows, x, y)
data += self.buff_type.pack_varint(length)
# random noise
mapdata = (random.choice([WHITE, BLACK]) for _ in range(length))
data += self.buff_type.pack(f'{length}B', *mapdata)
self.downstream.send_packet('map', data)
if (self.data[self.downstream.uuid]["frame"] == 100):
self.data[self.downstream.uuid]["animate"] = False
self.data[self.downstream.uuid]["lc"].stop()
print("anim end")
self.data[self.downstream.uuid]["frame"]+=1
def test_animate_map(self):
if self.data[self.downstream.uuid]["animate"]:
return
print("Starting anim")
self.data[self.downstream.uuid]["animate"] = True
self.data[self.downstream.uuid]["frame"] = 0
self.data[self.downstream.uuid]["lc"] = LoopingCall(self.ani_frame)
self.data[self.downstream.uuid]["lc"].start(0.1)
# from proxy_hide_chat.py
def read_chat(self, buff, direction):
buff.save()
if direction == "upstream":
p_text = buff.unpack_string()
return p_text
elif direction == "downstream":
p_text = str(buff.unpack_chat())
p_position = 0
# 1.8.x+
if self.upstream.protocol_version >= 47:
p_position = buff.unpack('B')
if p_position in (0, 1):
return p_text
class QuietDownstreamFactory(DownstreamFactory):
bridge_class = CustomBridge
motd = "Custom Proxy Server"
def main(argv):
# Parse options
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("-a", "--listen-host", default="", help="address to listen on")
parser.add_argument("-p", "--listen-port", default=25565, type=int, help="port to listen on")
parser.add_argument("-b", "--connect-host", default="127.0.0.1", help="address to connect to")
parser.add_argument("-q", "--connect-port", default=25565, type=int, help="port to connect to")
args = parser.parse_args(argv)
# Create factory
factory = QuietDownstreamFactory()
factory.connect_host = args.connect_host
factory.connect_port = args.connect_port
# Listen
factory.listen(args.listen_host, args.listen_port)
reactor.run()
if __name__ == "__main__":
import sys
main(sys.argv[1:])```
PLEASE RESPOND, I know its been two months but I relay hope I helped.
Thanks a lot for your answer. I'll try this when I can.
If this worked for you you can close the issue after you test it.