move proxy code into one file
This commit is contained in:
parent
88be34091a
commit
c7c4512108
61
capture.go
61
capture.go
|
@ -17,10 +17,13 @@ import (
|
|||
"github.com/google/subcommands"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var SrcIp_client = net.IPv4(127, 0, 0, 1)
|
||||
var SrcIp_server = net.IPv4(243, 0, 0, 2)
|
||||
var (
|
||||
SrcIp_client = net.IPv4(127, 0, 0, 1)
|
||||
SrcIp_server = net.IPv4(243, 0, 0, 2)
|
||||
)
|
||||
|
||||
func init() {
|
||||
register_command(&CaptureCMD{})
|
||||
|
@ -84,18 +87,15 @@ func (*CaptureCMD) Synopsis() string { return "capture packets in a pcap file" }
|
|||
func (p *CaptureCMD) SetFlags(f *flag.FlagSet) {
|
||||
f.StringVar(&p.server_address, "address", "", "remote server address")
|
||||
}
|
||||
|
||||
func (c *CaptureCMD) Usage() string {
|
||||
return c.Name() + ": " + c.Synopsis() + "\n" + SERVER_ADDRESS_HELP
|
||||
}
|
||||
|
||||
func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
address, hostname, err := server_input(c.server_address)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
}
|
||||
log := logrus.New()
|
||||
|
||||
listener, serverConn, clientConn, err := create_proxy(ctx, address)
|
||||
address, hostname, err := server_input(c.server_address)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
|
@ -112,44 +112,15 @@ func (c *CaptureCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interfac
|
|||
|
||||
_wl := sync.Mutex{}
|
||||
|
||||
go func() {
|
||||
defer listener.Disconnect(clientConn, "connection lost")
|
||||
defer serverConn.Close()
|
||||
|
||||
for {
|
||||
pk, err := clientConn.ReadPacket()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to read packet: %s\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
_wl.Lock()
|
||||
dump_packet(true, w, pk)
|
||||
_wl.Unlock()
|
||||
|
||||
err = serverConn.WritePacket(pk)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write packet: %s\n", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
pk, err := serverConn.ReadPacket()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to read packet: %s\n", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
err = create_proxy(ctx, log, address, nil, func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
|
||||
_wl.Lock()
|
||||
dump_packet(false, w, pk)
|
||||
dump_packet(toServer, w, pk)
|
||||
_wl.Unlock()
|
||||
|
||||
err = clientConn.WritePacket(pk)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write packet: %s\n", err)
|
||||
return 1
|
||||
}
|
||||
return pk, nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
|
|
@ -10,26 +10,7 @@ import (
|
|||
"github.com/df-mc/dragonfly/server/world/chunk"
|
||||
)
|
||||
|
||||
func chunkGetColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA {
|
||||
p := cube.Pos{int(x), int(y), int(z)}
|
||||
have_up := false
|
||||
p.Side(cube.FaceUp).Neighbours(func(neighbour cube.Pos) {
|
||||
if neighbour.X() < 0 || neighbour.X() >= 16 || neighbour.Z() < 0 || neighbour.Z() >= 16 {
|
||||
return
|
||||
}
|
||||
if !have_up {
|
||||
block_rid := c.Block(uint8(neighbour[0]), int16(neighbour[1]), uint8(neighbour[2]), 0)
|
||||
b, found := world.BlockByRuntimeID(block_rid)
|
||||
if found {
|
||||
if _, ok := b.(block.Air); !ok {
|
||||
if _, ok := b.(block.Water); !ok {
|
||||
have_up = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, cube.Range{int(y + 1), int(y + 1)})
|
||||
|
||||
func blockColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA {
|
||||
col := color.RGBA{0, 0, 0, 255}
|
||||
block_rid := c.Block(x, y, z, 0)
|
||||
if block_rid == 0 && y == 0 { // void
|
||||
|
@ -47,6 +28,30 @@ func chunkGetColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA {
|
|||
}
|
||||
*/
|
||||
}
|
||||
return col
|
||||
}
|
||||
|
||||
func chunkGetColorAt(c *chunk.Chunk, x uint8, y int16, z uint8) color.RGBA {
|
||||
p := cube.Pos{int(x), int(y), int(z)}
|
||||
have_up := false
|
||||
p.Side(cube.FaceUp).Neighbours(func(neighbour cube.Pos) {
|
||||
if neighbour.X() < 0 || neighbour.X() >= 16 || neighbour.Z() < 0 || neighbour.Z() >= 16 {
|
||||
return
|
||||
}
|
||||
if !have_up {
|
||||
block_rid := c.Block(uint8(neighbour[0]), int16(neighbour[1]), uint8(neighbour[2]), 0)
|
||||
if block_rid > 0 {
|
||||
b, found := world.BlockByRuntimeID(block_rid)
|
||||
if found {
|
||||
if _, ok := b.(block.Air); !ok {
|
||||
have_up = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, cube.Range{int(y + 1), int(y + 1)})
|
||||
|
||||
col := blockColorAt(c, x, y, z)
|
||||
|
||||
if have_up {
|
||||
if col.R > 10 {
|
||||
|
@ -75,11 +80,9 @@ func Chunk2Img(c *chunk.Chunk) *image.RGBA {
|
|||
col := chunkGetColorAt(c, x, height, z)
|
||||
|
||||
if height_liquid > height {
|
||||
bw := &block.Water{}
|
||||
wcol := bw.Color()
|
||||
col.R = col.R/2 + wcol.R/2
|
||||
col.G = col.G/2 + wcol.G/2
|
||||
col.B = col.B/2 + wcol.B/2
|
||||
bw := (&block.Water{}).Color()
|
||||
bw.A = uint8(clamp(int(127+(height_liquid-height)*5), 255))
|
||||
col = blendColors(col, bw)
|
||||
}
|
||||
|
||||
img.SetRGBA(int(x), int(z), col)
|
||||
|
|
|
@ -181,7 +181,7 @@ func (m *MapUI) Send(w *WorldState) error {
|
|||
}
|
||||
|
||||
m.send_lock.Unlock()
|
||||
return w.ClientConn.WritePacket(&packet.ClientBoundMapItemData{
|
||||
return w.proxy.client.WritePacket(&packet.ClientBoundMapItemData{
|
||||
MapID: VIEW_MAP_ID,
|
||||
Width: 128,
|
||||
Height: 128,
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/sandertv/gophertunnel/minecraft"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||
"github.com/sandertv/gophertunnel/minecraft/resource"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var G_disconnect_reason = "Connection lost"
|
||||
|
||||
type dummyProto struct {
|
||||
id int32
|
||||
ver string
|
||||
}
|
||||
|
||||
func (p dummyProto) ID() int32 { return p.id }
|
||||
func (p dummyProto) Ver() string { return p.ver }
|
||||
func (p dummyProto) Packets() packet.Pool { return packet.NewPool() }
|
||||
func (p dummyProto) ConvertToLatest(pk packet.Packet, _ *minecraft.Conn) []packet.Packet {
|
||||
return []packet.Packet{pk}
|
||||
}
|
||||
|
||||
func (p dummyProto) ConvertFromLatest(pk packet.Packet, _ *minecraft.Conn) []packet.Packet {
|
||||
return []packet.Packet{pk}
|
||||
}
|
||||
|
||||
type ProxyContext struct {
|
||||
server *minecraft.Conn
|
||||
client *minecraft.Conn
|
||||
listener *minecraft.Listener
|
||||
}
|
||||
type (
|
||||
PacketCallback func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error)
|
||||
ConnectCallback func(proxy *ProxyContext)
|
||||
)
|
||||
|
||||
func proxyLoop(ctx context.Context, proxy *ProxyContext, toServer bool, packetCBs []PacketCallback) error {
|
||||
var c1, c2 *minecraft.Conn
|
||||
if toServer {
|
||||
c1 = proxy.client
|
||||
c2 = proxy.server
|
||||
} else {
|
||||
c1 = proxy.server
|
||||
c2 = proxy.client
|
||||
}
|
||||
|
||||
for {
|
||||
if ctx.Err() != nil {
|
||||
return ctx.Err()
|
||||
}
|
||||
|
||||
pk, err := c1.ReadPacket()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, packetCB := range packetCBs {
|
||||
pk, err = packetCB(pk, proxy, toServer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if pk != nil {
|
||||
if err := c2.WritePacket(pk); err != nil {
|
||||
if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok {
|
||||
G_disconnect_reason = disconnect.Error()
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func create_proxy(ctx context.Context, log *logrus.Logger, server_address string, onConnect ConnectCallback, packetCB PacketCallback) (err error) {
|
||||
/*
|
||||
if strings.HasSuffix(server_address, ".pcap") {
|
||||
return create_replay_connection(server_address)
|
||||
}
|
||||
*/
|
||||
|
||||
GetTokenSource() // ask for login before listening
|
||||
|
||||
proxy := ProxyContext{}
|
||||
|
||||
var packs []*resource.Pack
|
||||
if G_preload_packs {
|
||||
fmt.Println("Preloading resourcepacks")
|
||||
serverConn, err := connect_server(ctx, server_address, nil, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to %s: %s", server_address, err)
|
||||
}
|
||||
serverConn.Close()
|
||||
packs = serverConn.ResourcePacks()
|
||||
fmt.Printf("%d packs loaded\n", len(packs))
|
||||
}
|
||||
|
||||
_status := minecraft.NewStatusProvider("Server")
|
||||
proxy.listener, err = minecraft.ListenConfig{
|
||||
StatusProvider: _status,
|
||||
ResourcePacks: packs,
|
||||
AcceptedProtocols: []minecraft.Protocol{
|
||||
dummyProto{id: 544, ver: "1.19.20"},
|
||||
},
|
||||
}.Listen("raknet", ":19132")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer proxy.listener.Close()
|
||||
|
||||
fmt.Printf("Listening on %s\n", proxy.listener.Addr())
|
||||
|
||||
c, err := proxy.listener.Accept()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
proxy.client = c.(*minecraft.Conn)
|
||||
|
||||
cd := proxy.client.ClientData()
|
||||
proxy.server, err = connect_server(ctx, server_address, &cd, false)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to %s: %s", server_address, err)
|
||||
}
|
||||
|
||||
// spawn and start the game
|
||||
if err := spawn_conn(ctx, proxy.client, proxy.server); err != nil {
|
||||
return fmt.Errorf("failed to spawn: %s", err)
|
||||
}
|
||||
|
||||
defer proxy.server.Close()
|
||||
defer proxy.listener.Disconnect(proxy.client, G_disconnect_reason)
|
||||
|
||||
if onConnect != nil {
|
||||
onConnect(&proxy)
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
// server to client
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := proxyLoop(ctx, &proxy, false, []PacketCallback{packetCB}); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
// client to server
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
if err := proxyLoop(ctx, &proxy, true, []PacketCallback{packetCB}); err != nil {
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
|
||||
wg.Wait()
|
||||
return nil
|
||||
}
|
2
rp.go
2
rp.go
|
@ -4,7 +4,7 @@ import "github.com/sandertv/gophertunnel/minecraft/resource"
|
|||
|
||||
func (w *WorldState) getPacks() (packs map[string]*resource.Pack, err error) {
|
||||
packs = make(map[string]*resource.Pack)
|
||||
for _, pack := range w.ServerConn.ResourcePacks() {
|
||||
for _, pack := range w.proxy.server.ResourcePacks() {
|
||||
packs[pack.Name()] = pack
|
||||
}
|
||||
return
|
||||
|
|
|
@ -2,13 +2,13 @@ package main
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/google/subcommands"
|
||||
"github.com/sandertv/gophertunnel/minecraft"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type SkinProxyCMD struct {
|
||||
|
@ -23,76 +23,33 @@ func (c *SkinProxyCMD) SetFlags(f *flag.FlagSet) {
|
|||
f.StringVar(&c.server_address, "address", "", "remote server address")
|
||||
f.StringVar(&c.filter, "filter", "", "player name filter prefix")
|
||||
}
|
||||
|
||||
func (c *SkinProxyCMD) Usage() string {
|
||||
return c.Name() + ": " + c.Synopsis() + "\n" + SERVER_ADDRESS_HELP
|
||||
}
|
||||
|
||||
func (c *SkinProxyCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{}) subcommands.ExitStatus {
|
||||
log := logrus.New()
|
||||
|
||||
address, hostname, err := server_input(c.server_address)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
}
|
||||
out_path := fmt.Sprintf("skins/%s", hostname)
|
||||
os.MkdirAll(out_path, 0o755)
|
||||
|
||||
listener, clientConn, serverConn, err := create_proxy(ctx, address)
|
||||
err = create_proxy(ctx, log, address, nil, func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
|
||||
if !toServer {
|
||||
process_packet_skins(proxy.client, out_path, pk, c.filter)
|
||||
}
|
||||
return pk, nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
out_path := fmt.Sprintf("skins/%s", hostname)
|
||||
|
||||
println("Connected")
|
||||
println("Press ctrl+c to exit")
|
||||
|
||||
os.MkdirAll(out_path, 0755)
|
||||
|
||||
errs := make(chan error, 2)
|
||||
go func() { // server -> client
|
||||
defer serverConn.Close()
|
||||
defer listener.Disconnect(clientConn, "connection lost")
|
||||
for {
|
||||
pk, err := serverConn.ReadPacket()
|
||||
if err != nil {
|
||||
if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok {
|
||||
_ = listener.Disconnect(clientConn, disconnect.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
process_packet_skins(clientConn, out_path, pk, c.filter)
|
||||
|
||||
if err = clientConn.WritePacket(pk); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() { // client -> server
|
||||
for {
|
||||
pk, err := clientConn.ReadPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err := serverConn.WritePacket(pk); err != nil {
|
||||
if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok {
|
||||
_ = listener.Disconnect(clientConn, disconnect.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case err := <-errs:
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
case <-ctx.Done():
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
|
128
utils.go
128
utils.go
|
@ -23,11 +23,11 @@ import (
|
|||
"unsafe"
|
||||
|
||||
"github.com/sandertv/gophertunnel/minecraft"
|
||||
|
||||
//"github.com/sandertv/gophertunnel/minecraft/gatherings"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol/login"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||
"github.com/sandertv/gophertunnel/minecraft/resource"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
|
@ -170,94 +170,6 @@ func spawn_conn(ctx context.Context, clientConn *minecraft.Conn, serverConn *min
|
|||
return nil
|
||||
}
|
||||
|
||||
type dummyProto struct {
|
||||
id int32
|
||||
ver string
|
||||
}
|
||||
|
||||
func (p dummyProto) ID() int32 { return p.id }
|
||||
func (p dummyProto) Ver() string { return p.ver }
|
||||
func (p dummyProto) Packets() packet.Pool { return packet.NewPool() }
|
||||
func (p dummyProto) ConvertToLatest(pk packet.Packet, _ *minecraft.Conn) []packet.Packet {
|
||||
return []packet.Packet{pk}
|
||||
}
|
||||
|
||||
func (p dummyProto) ConvertFromLatest(pk packet.Packet, _ *minecraft.Conn) []packet.Packet {
|
||||
return []packet.Packet{pk}
|
||||
}
|
||||
|
||||
func create_proxy(ctx context.Context, server_address string) (l *minecraft.Listener, clientConn, serverConn *minecraft.Conn, err error) {
|
||||
/*
|
||||
if strings.HasSuffix(server_address, ".pcap") {
|
||||
return create_replay_connection(server_address)
|
||||
}
|
||||
*/
|
||||
|
||||
GetTokenSource() // ask for login before listening
|
||||
|
||||
var packs []*resource.Pack
|
||||
if G_preload_packs {
|
||||
fmt.Println("Preloading resourcepacks")
|
||||
serverConn, err = connect_server(ctx, server_address, nil, true)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to connect to %s: %s", server_address, err)
|
||||
}
|
||||
serverConn.Close()
|
||||
packs = serverConn.ResourcePacks()
|
||||
fmt.Printf("%d packs loaded\n", len(packs))
|
||||
}
|
||||
|
||||
_status := minecraft.NewStatusProvider("Server")
|
||||
listener, err := minecraft.ListenConfig{
|
||||
StatusProvider: _status,
|
||||
ResourcePacks: packs,
|
||||
AcceptedProtocols: []minecraft.Protocol{
|
||||
dummyProto{id: 544, ver: "1.19.20"},
|
||||
},
|
||||
}.Listen("raknet", ":19132")
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
l = listener
|
||||
|
||||
fmt.Printf("Listening on %s\n", listener.Addr())
|
||||
|
||||
c, err := listener.Accept()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
clientConn = c.(*minecraft.Conn)
|
||||
|
||||
cd := clientConn.ClientData()
|
||||
serverConn, err = connect_server(ctx, server_address, &cd, false)
|
||||
if err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to connect to %s: %s", server_address, err)
|
||||
}
|
||||
|
||||
if err := spawn_conn(ctx, clientConn, serverConn); err != nil {
|
||||
return nil, nil, nil, fmt.Errorf("failed to spawn: %s", err)
|
||||
}
|
||||
|
||||
G_exit = append(G_exit, func() {
|
||||
serverConn.Close()
|
||||
l.Disconnect(clientConn, "Closing")
|
||||
clientConn.Close()
|
||||
l.Close()
|
||||
})
|
||||
/*
|
||||
go func() {
|
||||
for i := 0; i < 10; i++ {
|
||||
time.Sleep(1 * time.Second)
|
||||
clientConn.WritePacket(&packet.Text{
|
||||
TextType: packet.TextTypeTip,
|
||||
Message: a + "\n\n\n\n\n\n",
|
||||
})
|
||||
}
|
||||
}()
|
||||
*/
|
||||
return l, clientConn, serverConn, nil
|
||||
}
|
||||
|
||||
var PrivateIPNetworks = []net.IPNet{
|
||||
{
|
||||
IP: net.ParseIP("10.0.0.0"),
|
||||
|
@ -386,7 +298,7 @@ var muted_packets = []string{
|
|||
"*packet.AddActor",
|
||||
"*packet.UpdateAttributes",
|
||||
"*packet.Interact",
|
||||
"*packet.LevelEvent",
|
||||
//"*packet.LevelEvent",
|
||||
"*packet.SetActorData",
|
||||
"*packet.MoveActorDelta",
|
||||
"*packet.MovePlayer",
|
||||
|
@ -401,6 +313,7 @@ var muted_packets = []string{
|
|||
"*packet.SubChunkRequest",
|
||||
"*packet.Animate",
|
||||
"*packet.NetworkStackLatency",
|
||||
"*packet.InventoryTransaction",
|
||||
}
|
||||
|
||||
func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) {
|
||||
|
@ -428,6 +341,10 @@ func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) {
|
|||
switch pk := pk.(type) {
|
||||
case *packet.Disconnect:
|
||||
fmt.Printf("Disconnect: %s", pk.Message)
|
||||
case *packet.Event:
|
||||
fmt.Printf("Event: %d %+v\n", pk.EventType, pk.EventData)
|
||||
case *packet.LevelEvent:
|
||||
fmt.Printf("LevelEvent: %+v\n", pk)
|
||||
}
|
||||
fmt.Printf("%s 0x%x, %s\n", dir, pk.ID(), pk_name)
|
||||
}
|
||||
|
@ -438,3 +355,34 @@ func img2rgba(img *image.RGBA) []color.RGBA {
|
|||
header.Cap /= 4
|
||||
return *(*[]color.RGBA)(unsafe.Pointer(&header))
|
||||
}
|
||||
|
||||
// LERP is a linear interpolation function
|
||||
func LERP(p1, p2, alpha float64) float64 {
|
||||
return (1-alpha)*p1 + alpha*p2
|
||||
}
|
||||
|
||||
func blendColorValue(c1, c2, a uint8) uint8 {
|
||||
return uint8(LERP(float64(c1), float64(c2), float64(a)/float64(0xff)))
|
||||
}
|
||||
|
||||
func blendAlphaValue(a1, a2 uint8) uint8 {
|
||||
return uint8(LERP(float64(a1), float64(0xff), float64(a2)/float64(0xff)))
|
||||
}
|
||||
|
||||
func blendColors(c1, c2 color.RGBA) (ret color.RGBA) {
|
||||
ret.R = blendColorValue(c1.R, c2.R, c2.A)
|
||||
ret.G = blendColorValue(c1.G, c2.G, c2.A)
|
||||
ret.B = blendColorValue(c1.B, c2.B, c2.A)
|
||||
ret.A = blendAlphaValue(c1.A, c2.A)
|
||||
return ret
|
||||
}
|
||||
|
||||
func clamp(a, b int) int {
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
if a < 0 {
|
||||
return 0
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
|
203
world.go
203
world.go
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"hash/crc32"
|
||||
|
@ -23,7 +22,6 @@ import (
|
|||
"github.com/df-mc/goleveldb/leveldb/opt"
|
||||
"github.com/go-gl/mathgl/mgl32"
|
||||
"github.com/google/subcommands"
|
||||
"github.com/sandertv/gophertunnel/minecraft"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol"
|
||||
"github.com/sandertv/gophertunnel/minecraft/protocol/packet"
|
||||
"github.com/sandertv/gophertunnel/minecraft/resource"
|
||||
|
@ -42,6 +40,7 @@ type TPlayerPos struct {
|
|||
// the state used for drawing and saving
|
||||
|
||||
type WorldState struct {
|
||||
ctx context.Context
|
||||
ispre118 bool
|
||||
voidgen bool
|
||||
chunks map[protocol.ChunkPos]*chunk.Chunk
|
||||
|
@ -51,11 +50,11 @@ type WorldState struct {
|
|||
WorldName string
|
||||
ServerName string
|
||||
worldCounter int
|
||||
withPacks bool
|
||||
packs map[string]*resource.Pack
|
||||
|
||||
PlayerPos TPlayerPos
|
||||
ClientConn *minecraft.Conn
|
||||
ServerConn *minecraft.Conn
|
||||
PlayerPos TPlayerPos
|
||||
proxy *ProxyContext
|
||||
|
||||
log *logrus.Logger
|
||||
|
||||
|
@ -110,13 +109,13 @@ var IngameCommands = map[string]IngameCommand{
|
|||
|
||||
func setnameCommand(w *WorldState, cmdline []string) bool {
|
||||
w.WorldName = strings.Join(cmdline, " ")
|
||||
send_message(w.ClientConn, fmt.Sprintf("worldName is now: %s", w.WorldName))
|
||||
send_message(w.proxy.client, fmt.Sprintf("worldName is now: %s", w.WorldName))
|
||||
return true
|
||||
}
|
||||
|
||||
func toggleVoid(w *WorldState, cmdline []string) bool {
|
||||
w.voidgen = !w.voidgen
|
||||
send_message(w.ClientConn, fmt.Sprintf("using void generator: %t", w.voidgen))
|
||||
send_message(w.proxy.client, fmt.Sprintf("using void generator: %t", w.voidgen))
|
||||
return true
|
||||
}
|
||||
|
||||
|
@ -166,14 +165,25 @@ func (c *WorldCMD) Execute(ctx context.Context, f *flag.FlagSet, _ ...interface{
|
|||
return 1
|
||||
}
|
||||
|
||||
listener, clientConn, serverConn, err := create_proxy(ctx, server_address)
|
||||
w := NewWorldState()
|
||||
w.log = c.log
|
||||
w.voidgen = c.enableVoid
|
||||
w.ServerName = hostname
|
||||
w.withPacks = c.packs
|
||||
w.ctx = ctx
|
||||
|
||||
err = create_proxy(ctx, c.log, server_address, w.OnConnect, func(pk packet.Packet, proxy *ProxyContext, toServer bool) (packet.Packet, error) {
|
||||
if toServer {
|
||||
pk = w.ProcessPacketClient(pk)
|
||||
} else {
|
||||
pk = w.ProcessPacketServer(pk)
|
||||
}
|
||||
return pk, nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
return 1
|
||||
}
|
||||
|
||||
c.handleConn(ctx, listener, clientConn, serverConn, hostname)
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
@ -215,7 +225,7 @@ func (w *WorldState) ProcessLevelChunk(pk *packet.LevelChunk) {
|
|||
max = int(pk.HighestSubChunk)
|
||||
}
|
||||
|
||||
w.ServerConn.WritePacket(&packet.SubChunkRequest{
|
||||
w.proxy.server.WritePacket(&packet.SubChunkRequest{
|
||||
Dimension: int32(w.Dim.EncodeDimension()),
|
||||
Position: protocol.SubChunkPos{
|
||||
pk.Position.X(), 0, pk.Position.Z(),
|
||||
|
@ -352,7 +362,7 @@ func (w *WorldState) SaveAndReset() {
|
|||
|
||||
// set gamerules
|
||||
ld := provider.LevelDat()
|
||||
gd := w.ServerConn.GameData()
|
||||
gd := w.proxy.server.GameData()
|
||||
for _, gr := range gd.GameRules {
|
||||
switch gr.Name {
|
||||
case "commandblockoutput":
|
||||
|
@ -453,25 +463,20 @@ func (w *WorldState) SaveAndReset() {
|
|||
w.Reset()
|
||||
}
|
||||
|
||||
func (c *WorldCMD) handleConn(ctx context.Context, l *minecraft.Listener, cc, sc *minecraft.Conn, server_name string) {
|
||||
var err error
|
||||
w := NewWorldState()
|
||||
w.ServerName = server_name
|
||||
w.ClientConn = cc
|
||||
w.ServerConn = sc
|
||||
w.voidgen = c.enableVoid
|
||||
w.log = c.log
|
||||
func (w *WorldState) OnConnect(proxy *ProxyContext) {
|
||||
w.proxy = proxy
|
||||
|
||||
if c.packs {
|
||||
if w.withPacks {
|
||||
fmt.Println("reformatting packs")
|
||||
go func() {
|
||||
w.packs, err = w.getPacks()
|
||||
w.packs, _ = w.getPacks()
|
||||
}()
|
||||
}
|
||||
|
||||
{ // check game version
|
||||
gd := w.ServerConn.GameData()
|
||||
gd := w.proxy.server.GameData()
|
||||
gv := strings.Split(gd.BaseGameVersion, ".")
|
||||
var err error
|
||||
if len(gv) > 1 {
|
||||
var ver int
|
||||
ver, err = strconv.Atoi(gv[1])
|
||||
|
@ -483,117 +488,75 @@ func (c *WorldCMD) handleConn(ctx context.Context, l *minecraft.Listener, cc, sc
|
|||
if w.ispre118 {
|
||||
fmt.Println("using legacy (< 1.18)")
|
||||
}
|
||||
|
||||
dim_id := gd.Dimension
|
||||
if w.ispre118 {
|
||||
dim_id += 10
|
||||
}
|
||||
w.Dim = dimension_ids[uint8(dim_id)]
|
||||
}
|
||||
|
||||
send_message(w.ClientConn, "use /setname <worldname>\nto set the world name")
|
||||
send_message(w.proxy.client, "use /setname <worldname>\nto set the world name")
|
||||
|
||||
G_exit = append(G_exit, func() {
|
||||
w.SaveAndReset()
|
||||
})
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() { // client loop
|
||||
defer func() { done <- struct{}{} }()
|
||||
for {
|
||||
skip := false
|
||||
pk, err := w.ClientConn.ReadPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
switch pk := pk.(type) {
|
||||
case *packet.MovePlayer:
|
||||
w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw)
|
||||
case *packet.PlayerAuthInput:
|
||||
w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw)
|
||||
case *packet.MapInfoRequest:
|
||||
if pk.MapID == VIEW_MAP_ID {
|
||||
w.ui.Send(w)
|
||||
skip = true
|
||||
}
|
||||
case *packet.MobEquipment:
|
||||
if pk.NewItem.Stack.NBTData["map_uuid"] == int64(VIEW_MAP_ID) {
|
||||
skip = true
|
||||
}
|
||||
case *packet.Animate:
|
||||
w.ProcessAnimate(pk)
|
||||
case *packet.CommandRequest:
|
||||
skip = w.ProcessCommand(pk)
|
||||
}
|
||||
|
||||
if !skip {
|
||||
if err := w.ServerConn.WritePacket(pk); err != nil {
|
||||
if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok {
|
||||
_ = l.Disconnect(w.ClientConn, disconnect.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() { // send map item
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
case <-w.ctx.Done():
|
||||
return
|
||||
default:
|
||||
for {
|
||||
time.Sleep(1 * time.Second)
|
||||
err := w.ClientConn.WritePacket(&MAP_ITEM_PACKET)
|
||||
t := time.NewTimer(1 * time.Second)
|
||||
for range t.C {
|
||||
err := w.proxy.client.WritePacket(&MAP_ITEM_PACKET)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() { // server loop
|
||||
defer w.ServerConn.Close()
|
||||
defer l.Disconnect(w.ClientConn, "connection lost")
|
||||
defer func() { done <- struct{}{} }()
|
||||
|
||||
gd := w.ServerConn.GameData()
|
||||
dim_id := gd.Dimension
|
||||
if w.ispre118 {
|
||||
dim_id += 10
|
||||
}
|
||||
w.Dim = dimension_ids[uint8(dim_id)]
|
||||
|
||||
for {
|
||||
pk, err := w.ServerConn.ReadPacket()
|
||||
if err != nil {
|
||||
if disconnect, ok := errors.Unwrap(err).(minecraft.DisconnectError); ok {
|
||||
_ = l.Disconnect(w.ClientConn, disconnect.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch pk := pk.(type) {
|
||||
case *packet.ChangeDimension:
|
||||
w.ProcessChangeDimension(pk)
|
||||
case *packet.LevelChunk:
|
||||
w.ProcessLevelChunk(pk)
|
||||
w.ui.Send(w)
|
||||
send_popup(w.ClientConn, fmt.Sprintf("%d chunks loaded\nname: %s", len(w.chunks), w.WorldName))
|
||||
case *packet.SubChunk:
|
||||
w.ProcessSubChunk(pk)
|
||||
case *packet.AvailableCommands:
|
||||
for _, ic := range IngameCommands {
|
||||
pk.Commands = append(pk.Commands, ic.cmd)
|
||||
}
|
||||
}
|
||||
|
||||
if err := w.ClientConn.WritePacket(pk); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (w *WorldState) ProcessPacketClient(pk packet.Packet) packet.Packet {
|
||||
switch pk := pk.(type) {
|
||||
case *packet.MovePlayer:
|
||||
w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw)
|
||||
case *packet.PlayerAuthInput:
|
||||
w.SetPlayerPos(pk.Position, pk.Pitch, pk.Yaw, pk.HeadYaw)
|
||||
case *packet.MapInfoRequest:
|
||||
if pk.MapID == VIEW_MAP_ID {
|
||||
w.ui.Send(w)
|
||||
pk = nil
|
||||
}
|
||||
case *packet.MobEquipment:
|
||||
if pk.NewItem.Stack.NBTData["map_uuid"] == int64(VIEW_MAP_ID) {
|
||||
pk = nil
|
||||
}
|
||||
case *packet.Animate:
|
||||
w.ProcessAnimate(pk)
|
||||
case *packet.CommandRequest:
|
||||
if w.ProcessCommand(pk) {
|
||||
pk = nil
|
||||
}
|
||||
}
|
||||
return pk
|
||||
}
|
||||
|
||||
func (w *WorldState) ProcessPacketServer(pk packet.Packet) packet.Packet {
|
||||
switch pk := pk.(type) {
|
||||
case *packet.ChangeDimension:
|
||||
w.ProcessChangeDimension(pk)
|
||||
case *packet.LevelChunk:
|
||||
w.ProcessLevelChunk(pk)
|
||||
w.ui.Send(w)
|
||||
send_popup(w.proxy.client, fmt.Sprintf("%d chunks loaded\nname: %s", len(w.chunks), w.WorldName))
|
||||
case *packet.SubChunk:
|
||||
w.ProcessSubChunk(pk)
|
||||
case *packet.AvailableCommands:
|
||||
for _, ic := range IngameCommands {
|
||||
pk.Commands = append(pk.Commands, ic.cmd)
|
||||
}
|
||||
}
|
||||
return pk
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue