From f1cb7df05e30f6cce4c0eec158ddc5649d06a838 Mon Sep 17 00:00:00 2001 From: olebeck <31539311+olebeck@users.noreply.github.com> Date: Sun, 2 Apr 2023 16:49:24 +0200 Subject: [PATCH] make subcommands more modular --- subcommands/capture.go | 45 +----- subcommands/chat_log.go | 33 +--- subcommands/debug.go | 4 +- subcommands/resourcepack-d/resourcepack-d.go | Bin 5544 -> 5674 bytes subcommands/skins/skins.go | 156 ++----------------- subcommands/world/world.go | 56 +++---- utils/handlers/capture.go | 69 ++++++++ utils/handlers/chat_log.go | 54 +++++++ utils/handlers/skins.go | 127 +++++++++++++++ utils/packet_logger.go | 4 - utils/proxy.go | 156 ++++++++++++++----- utils/replay.go | 44 +++++- {subcommands/skins => utils}/skin.go | 2 +- {subcommands/skins => utils}/skinpack.go | 18 ++- utils/utils.go | 5 +- 15 files changed, 479 insertions(+), 294 deletions(-) create mode 100644 utils/handlers/capture.go create mode 100644 utils/handlers/chat_log.go create mode 100644 utils/handlers/skins.go rename {subcommands/skins => utils}/skin.go (99%) rename {subcommands/skins => utils}/skinpack.go (89%) diff --git a/subcommands/capture.go b/subcommands/capture.go index c21a774..09b2220 100644 --- a/subcommands/capture.go +++ b/subcommands/capture.go @@ -1,41 +1,18 @@ package subcommands import ( - "bytes" "context" - "encoding/binary" "flag" - "fmt" - "io" - "net" - "os" - "sync" - "time" "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" - - "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/bedrock-tool/bedrocktool/utils/handlers" ) func init() { utils.RegisterCommand(&CaptureCMD{}) } -var dumpLock sync.Mutex - -func dumpPacket(f io.WriteCloser, toServer bool, payload []byte) { - dumpLock.Lock() - defer dumpLock.Unlock() - f.Write([]byte{0xAA, 0xAA, 0xAA, 0xAA}) - packetSize := uint32(len(payload)) - binary.Write(f, binary.LittleEndian, packetSize) - binary.Write(f, binary.LittleEndian, toServer) - binary.Write(f, binary.LittleEndian, time.Now().UnixMilli()) - f.Write(payload) - f.Write([]byte{0xBB, 0xBB, 0xBB, 0xBB}) -} - type CaptureCMD struct { ServerAddress string } @@ -52,26 +29,10 @@ func (c *CaptureCMD) Execute(ctx context.Context, ui utils.UI) error { return err } - os.Mkdir("captures", 0o775) - fio, err := os.Create(fmt.Sprintf("captures/%s-%s.pcap2", hostname, time.Now().Format("2006-01-02_15-04-05"))) - if err != nil { - return err - } - defer fio.Close() - utils.WriteReplayHeader(fio) - proxy, err := utils.NewProxy() if err != nil { return err } - proxy.PacketFunc = func(header packet.Header, payload []byte, src, dst net.Addr) { - IsfromClient := src.String() == proxy.Client.LocalAddr().String() - - buf := bytes.NewBuffer(nil) - header.Write(buf) - buf.Write(payload) - dumpPacket(fio, IsfromClient, buf.Bytes()) - } - - return proxy.Run(ctx, address) + proxy.AddHandler(handlers.NewPacketCapturer()) + return proxy.Run(ctx, address, hostname) } diff --git a/subcommands/chat_log.go b/subcommands/chat_log.go index 4e69c09..c48ea93 100644 --- a/subcommands/chat_log.go +++ b/subcommands/chat_log.go @@ -3,15 +3,10 @@ package subcommands import ( "context" "flag" - "fmt" - "os" - "time" "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/utils" - - "github.com/sandertv/gophertunnel/minecraft/protocol/packet" - "github.com/sirupsen/logrus" + "github.com/bedrock-tool/bedrocktool/utils/handlers" ) type ChatLogCMD struct { @@ -32,34 +27,12 @@ func (c *ChatLogCMD) Execute(ctx context.Context, ui utils.UI) error { return err } - filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07")) - f, err := os.Create(filename) - if err != nil { - return err - } - defer f.Close() - proxy, err := utils.NewProxy() if err != nil { return err } - proxy.PacketCB = func(pk packet.Packet, toServer bool, t time.Time) (packet.Packet, error) { - if text, ok := pk.(*packet.Text); ok { - logLine := text.Message - if c.Verbose { - logLine += fmt.Sprintf(" (TextType: %d | XUID: %s | PlatformChatID: %s)", text.TextType, text.XUID, text.PlatformChatID) - } - f.WriteString(fmt.Sprintf("[%s] ", t.Format(time.RFC3339))) - logrus.Info(logLine) - if toServer { - f.WriteString("SENT: ") - } - f.WriteString(logLine + "\n") - } - return pk, nil - } - - return proxy.Run(ctx, address) + proxy.AddHandler(handlers.NewChatLogger()) + return proxy.Run(ctx, address, hostname) } func init() { diff --git a/subcommands/debug.go b/subcommands/debug.go index c935bdb..a2b4e0c 100644 --- a/subcommands/debug.go +++ b/subcommands/debug.go @@ -19,7 +19,7 @@ func (c *DebugProxyCMD) SetFlags(f *flag.FlagSet) { } func (c *DebugProxyCMD) Execute(ctx context.Context, ui utils.UI) error { - address, _, err := utils.ServerInput(ctx, c.ServerAddress) + address, hostname, err := utils.ServerInput(ctx, c.ServerAddress) if err != nil { return err } @@ -30,7 +30,7 @@ func (c *DebugProxyCMD) Execute(ctx context.Context, ui utils.UI) error { if err != nil { return err } - return proxy.Run(ctx, address) + return proxy.Run(ctx, address, hostname) } func init() { diff --git a/subcommands/resourcepack-d/resourcepack-d.go b/subcommands/resourcepack-d/resourcepack-d.go index 9bd0cc4ca90e5d308f4f5b96a158fac6a47df7c0..81ddb9486c42a9df960f625c8602c9a5f1c57866 100644 GIT binary patch literal 5674 zcmV+_7S-thM@dveQdv+`00mVBS%=PRZoq>6KD%o+dP0#<6Ci&g3^8Hv&{|E%{g2O| z`b^HrZLYAuBz9VeYb+Agr7$`YrB*}<2H^3uRm(JILxfotv6C^m-Owt1Fn24Xwn=LF zS8HE~?{NLg5RB3`ta&y|kKi#Rn~cMFw0V#jr8RMu^9DqA*-z`d&+0Vn%T+K`Pggkn z-0HjU%$hq(P4E*3GdYmw%3Q;+@4!?UqW>+$px@}Tn6qJF#BZsJ^XhTqEv_&C|KVy2 zaBb`RiS#~k zF?0|XJg2yq(v}PG2!2rzCTb>RZ6*ysBHyxREA4w&EGv+y*tW_XH~YjnCo2XL(7SPl zrRUh|dR;~Ex@Np9A66|>I+;DF7XHZ!R53mjDPY8IIaBz#eW)qA?mB)=XtCpFO=S4~ zaZ4sVr?-tlkH-->m5&BX%#ut)wOA0rek>zuPdgt4ErbO2-KnM}s_RmBqNbTcN*LDm z-tN~S=yq^%e8VmGC7w1wOnt>c|Iuv7@d_jzb+roX<*2x2LCB zI~D$PL7d+E*B(AoR@fnENsBZ|QA=Os1;q@p!dzuABR*~%+k-x z&#eV9iOJjanm;fp7*HH7m_}^i|4b*5-;OTQcLpyl7}S(qKv`g8$Zu^x!ovNLT>UCA z6_9_76%HcI^3{K4dXJyBydTIx3_xMEctr@=D+Kk16K#(zKn5sAhV1IU+s_>%I}{XG zJz3m_jkHMBvjU`i#XZZfo;dy_QBBigwHqUq_^jpv*GtsTOOAm1#!UFBFhxaG=1;95 z|5~*wHYAzTF+>l?Y)XkIo2Mf^QUiY8)tHf;mt5OIPubGV-|G!~$r(6oO2us9HSWTB z$FvFQDv)IbEiY4$LHAb-lh^AZq!A;I;7G4g`%qWH2&CN!s%50AV2sUEr2Lkj$B8D0 zN=^6kwWkbZY%*n{#Df)Hg|P$+oUM}!!Oxz3?3hhc9Sk);wk5a$ushqe$v(7Zahw@$DaYXQttgf8CfKU*;uTRWG-Ol{0k8;(8w}ObphgQjKC@~9L$~u+kb-Ke;$K{Q)UG6J|rG5zTP;2a6LPu zqdbrI-ek?g7uAJhFI4AYvphS8Z@tN}*ng)YPznmYR~jcYkNhakg~CpWbd6{>0qVCD z!bqKtAN$~w#O?|>GEm{U{{#p$Vf0&9;A6U-Y>T!*$7gsk7*Jt13J^=i^*rp+;426fE0&GgZl8?`meETI=vN&PmxZ z6kkVsL0xnwg;Ue(Yei^p)F6|1vU9=-L?gdcH(l$1{KAI+dg^u7nzS&aR0af$wAt}tQ#t`z$VD<>Sw8*YtQR{>nReci>b!#1QMpG=BZeoc$*XVy4^#donKMSKZjZ$|+beVupuw zTD3z*rsq-wa3N9b80+}VPQXJ1FG-hu9^=jX!`ALH#WSefMtb(GxaV3w471^ope)Bh z?S3FKpW3Z;GgjSeRk|9QX$Y2Z%oZDqboz_m5j9v|FfB-0)=~t_1ixI_Qlu;& zz1>0YrW3FH(*!CpZa>T?!<=0qP(%@imRc1OYB-u$Y^?U-xw1XNGbyjnP2?jZbOCv} z542WriV7w-$rc?*tc1b5;e&>uETk|`M;9A0+dO4|y~#q{wY4T`YtA@2_(FwTuQ+>GLXMKZkN4cacp%fUlTFZGNu~@t zc;msoOf*$3%e-0r0Oc@Mk6`t+ku`>m|0e+JN$QAO62$-j$d#38gES(t@cS)Of-$na zR+^xPnkO{#QkKzXzr$>9{qL^rs4Br1$z4jA>wT>IIX+`&5FWd>yIG%0B~Y3+vuNUd z=gf7l)RX?r*er^rM$3`SK4*&=HF{gZFq~;a@Ca5l97p%RPhfI&id>yruU+ zy*_kPgwP=_tM0fj@7X=_a_Lu=7*?1Ooz2>#2N62V&-ty+-pVGc6`;hE!ZvYHRW0o) z+Mu*~^}n0=5T8Vj6Q5lB*i=xRD8JmfL}W>Tl;{xaDeD zyqIZwogs6vHIsZB=on)*^pss@N0wbtb}^tkcKfIT(n}e+f06_xYy?)F)y&qNuA`JN zj68UUsNF4GDpw!A;HZ-Q~nZYeFlka5mi| z(7|)f@3Xo{NB(|TYt2huoZ)a1+x4}&VeQ5ncQ77W+OZRtx>pDw<2s9G4pWlX!yr4= zQ^?ayejCa`6e~7O(XZm;xVmSTC4A8n2ZPADY9GmTlwjejV-8a8PDq!uSlK3aW9H{4 zVga5z0MTiK28$a&!L%5YcSSAOee4V`Wj3xe;)8KF>-w8W6G|H&j>1m`A zd{sE+!pO}dc37%4Y#TP0INP9)bu_mkj{+-SepsRKztg~RG!ED^M^%tO_S;*uxl+Ve zgcziLWDopj8$~3J6gW?Z7b@d6HxqLOhR^!t0+MKAu6^#1t?|Kf?<76Ca zbnT7aX|Yu1x9uQzfH|2a4}ev#NeqMR@Y^F46C9VL?8pt>r5O)x`3U00qX^Rzj&5+SA(D{(@17AB}=sWOqq~qLD2Q zdMfEife$ZHqjDDIeb$niKJvWs1Dr*l`s+t14xMYXICGLx;Q9Fc>MQ$&BtpPj${&3u z#F-3_OV;EE=Z_xg$b!9KNx0`_O7Yu~XB1tEH<$GG*i`>dQik$EMo+H7=c3{k z!4vK240=1>1*r7%DvswsR+96NH#j)2n1e*Kf9w9P3DLrG_sVUy6b1#Fp zBpUPu;mYWl0bNM`IAFMi7*I>|y0pZBDnIma;%5{pIT#@WXWeOqKcq8S@9GJlusSjK z8%BgMV$rO6TqKk7+l^V&0W1RHYPf6X^79!}hCDZT`|Q_z2zz78i`{zsOedq=VD*rP zlFs4*AQ!2bxeh!O>ZyD7(#r`!|4p%(vi!MPh*LvJ@IWdG9AE=6B+vVek6so8T_?1$ z>TU-vB%kOEq}4)6z#Th6r|I*>&8H_Q5lqd7BF>8gd+_yegeOu}Na{7M58gdo)r&K9 z%g14%nz>2`V_ffs=g#O#c9A&T4sJ)yUa-MLl@-8^y28Vigpr$j0L8vb!vu=u%c~me zT2p;)1Q#R4RbrCP4;lB7+SSJF%C&*w!;}3DHhnXy+2=Lmed6E-B!=Az^ubjc^jtq= zY#Q%a)id||Fyp(ZEt$+xu0!@vBB<0Qp56r%g=A%Z0P)*<2w6i%zM;}qhOqSuD>S%z zpK6D%lT!)xp2nn*Bq#|qa4%ohZ4WM&+N^UID-se4mtSN881;~tx`5TMjEOBw;j|tO zJTd;>BB{`|j+AM&1ARtCbZ_4jt;O-cmm7l}mlA2a8L%wqFI zvnT1wx~p}$Y=?#vMEsFha%i>s>tyTIY2%@??j;MuNNnCY>Ow=6#Y1v7EcV{OGkmzi zn7b1FFX})1;7fybkcUS@&E2q)XbE+_kVFIX?nQ8kx(Rt4N7ALq{+7IZ%bHbC#`@~R zxdGDUqO?se2&k<+X9v_yZNJMuLnib^rH6NnQAd`b)T?2R^VM9) zT#k2adjj)ZnK9TRs9nv6>QU&q;X(%KG=?d~lj&jI_v;}XOlaK0{nl;V1yGqC>`Jvy zzDJ0p%)Xud8Kwrg6pgP0RQ1hFU}Z#XjQisS?v$o+p>8)jkc)xj7p$$UpK7-P zvgC(3@Y8<2Dje5_xz{3Zz&R%6Wv9&Lkk?E8;PF&hLoWgiq29rT$Tkca+2ks;aEAzmVW!r{%k+Szw?O?XnOO!p8`?*T{(ojT zZX`Ww+Kl~Y6LVM3p1e<7Lv9+x^I)b0d>8Zj^Rd^Rhoas?49FoqEs8AIwnY^4Vs;|E zeMw4|n&z+Bh6;=*1Ar8v-Ss4;=3KZ`@G7dmU{#wDqT>f0Wd*;g0fl<{aZpFO&=FaY zHlu%7lMH&o2n5PD2%Wmm##X*4ExOgk^k++)U0nu;H*K5VEA)8`i0S%JahqX46#5$d z8wbyE91}5Uo^7mxE=uPy9t$ z>F}q&i9)!ir_puZ2}3iw{A3kE4^zDnV~m~vsi4_Ysq%2-a!%i{HRykN^Jb_lZdp#- zOwI z(Upx;xfavl%H`*x#~z9JjFtxV^ZSg()(J5h4^3bVcZGlshZ@nedvrr)9=Lf6-#nzd z97eX2wE#4GWnrRFBrFz6<658J;s~w^CO$J=JytyRVJbb}Mj_a%13lzI zgyS*ZwK|)45Dx)xBx$JqA9OUW0osW#Hn-YhK%syQx?G1iV4n%@?>m(_K#nU}(>Nf%%_o1ozkAS!LlQBwL&&9dGsane{adH96m z58X+nc&^!>1}-9HnmsXD@ARacQg!rOh-;FX6TViy)3J}m#JZ~{*effxpIsA@v}0U3gC%$TN@+Zyb+yXLmm-0czb_!7Qqmg70>4T?jACx zfT&R6#kPC)1s$)G!4%Qw)F6LXo!jKfnW&XZ5Fkb#{sEY#V$qkc2WS*7GfM$)n;=O^ zhR`j^RHibrp2X0ty;S>xcM?clF&^2{-AC%Q!l>TmG7{%^u+oS@&+HM?PBd9{i^ws7 zE46Z~dhUAk+%x>!__`>tK^gCD;=p!02RO$wMP{cv&2L;opkJM6A&!PcvKz$5O9F> z9a&@9c@lVpKSoSs8br5gt;;`t2k|nOI*_K`$yfw=2Sc4g#mP$Jrixwp-A{PC5UR;6Hzx48V41Fc z!2nJFuLwjM{+|3{VphKkbQ>Pkxu^7ugP#o;qabs-$@;*JL_fIGbq8w&1ve^TeGLhY zaI@;rL8lSsRNOdax4*F*$AXyk#De1kb-?7V;5xKZE@_|@wW3)hKk9juuy>SK=>cIf zaWNV+?j7$9$dXE+pcdnry$sd?qcJK0<1jOcSv%=MJ;@jIhJIqrQEr2NjiC`n7gr&k zFWRE9W9gY!HSTg_QPf00$w{)p#PxD5$R{;3f;VV8%1U%1=I3rmAK_&om@zfD^B zoFlg)s@p6pd%yB`rsfIFYeKF6(&+L7Tz(qU>GtYqCX==#Sas;?XCtm4c6#1Wv?L&? zkj%?3r%T7K5UO4(-SA9a3QOtH2L*i#cIkrYEeY4c3b)FALV`iHL4`N7i^&9RG2q{6 z&{^igaff$y!F7^#3%$UQc@UI&lQu#VMwNwlMC2_kkNOF zX_s}&M{3Db9AMn>Xl@?pTw8X*ot4K^et%l$soY~JW*v`yLiW{+!E`@(md)HoUAuZM z*zJp1G@BZ#jgHSrz2;Dlixeaty%eB-pU`e3O{!bb`~+My&+ zopVwWlf!}*8s$hSNm4y}fro}$Ow7C>=7Bx*U^9v0u$c?dpD@3fAn)}s?dn39`gA&D zC_m>;2>B{aoAr#B1Q*NAHO9(_n6IH_YcQ*5edjc=46=~_K006^T%Bq0n);iGqwr=( z@+r3BJDA5pz1(Mmym{l_yJ)Gy_W{p7c8asHmk5C9(4~L{PUIZQrcvWgq$#F9SYFXo zZm`Hq0`IzuYKl4LgOE7ASfYbIYSrV}y!c)22Qi(rMe#{L!x|5o4XA&ON(dz$`>#3G zB$7Q>7X$KL#&YCjvb{PI(wG}m+;YwlK#l6_qC!{u{0S`9@o#WRHV8!p1T~bt^=@gu zneR%&SuJ*W7DP=SRC(X@g7Rk{-7nM21uFv;qNjrj{TG?u;h6CeBs2x{f<}0u(h}Nl z+XK{^hs-a&m7=l2GDlRGcXmfg+|BN4s|UXk7ZuvJza9>?t2Kn%*yRyda%WhaU+%KF zsSp+>n(nSF1yl&m*!aX?(K92W+Or&=dGv;YbUX{0(TP~qDU21cgezp-;$3L2#DQn~ zEGR9x6Ac1X|FdY8u_n61`sH;T)$`(41gf*-Sk#s`BjZxdn_xB8c*=vss`7{T%0@FX zn6}t)13_rgA6J;eXQ1ajyk0WU+ms59syI)PVYI&OX976@;|*Nsb4{iX@`=tzZM90T z9yXbzt8v^5G#0cwmZx%89C1MOn*IzWEQ2DN45I~&E{wFO$_{Ci;%L|(<^G*|8>E{& zV2_vQEv5$wDPxa?i(qs_6yueHhjnzMP9sAbGZrpWR~tzVa!x}YXVG3|d7IDVW9fq0 z2qZlWz(ve_iMtr{tFg;TkB_s+oZ8=nw+#Wj^Oh#fjn~bSd~W5BiHUfqoRRsZ)PRk$ ziN1>m&MHFdRkUSq_AEq4H-h6RBK|L{(xDc}C@msHi<^1G$M4=-$M6@K?I-%gKy`%< zJjRo5MH=6p!krim?y~JxN-i0HrDm}U0s+%+_93%Y9>r35hPWz468sySLzV`&P_bJ@ zJ2i15w_&v@JG%t=Gu#op9Nyjgw3IKlG~jZbICHp4yHf~ZI@#d4@8-GDG7%z$6BkdZ z=QS?a_c5Jn5Vw7-)s~ms-yavQxF&?FD&hD;NAJ88hUtQU^wMFKpw58OM3a|GpP8R~`;s4d3XEaAu9=PJ~NQwj4W? z0&WRT*SYucFGQjxsAIvz16S0K&4A@$fsDCvsRP)t3!mywM7SW+rPuSGXVBNZ=bh`q zB3rdmhPaiyEo`6oHwi+PR(yRI2;U2f3hUmDSXb851l=bv#IPhp%FqFFIw3D&lMRW7 z){=+0eclTzQWucyikEPFvJy395tD;(yQ65|?EPkuYc}uWD#mk(m4>?su2`4E?e8T| zT~Y;k-qoJO*@-g>3tfQ5aP1{AoLToYDpr%C^caYS10h+^{saG3uK8m!oq?BzCcsGq znxXkjzrPWzS7&w`p!mQ4d0*_oO;|SfG9xw)>Yeq-7jns>DLa8oIee!I+E_x7jlYih z12Ulfgx4+7Gp%q~eDHmBZmDl|DERTjRe8zj5}dI$r;Rgg2gtliIm%fat|s?zE@a`7 zSJR2+P@WqnUwocZ0v#*OWj2bg%N=iBB!B$+9sStk>~|%D-0}dkxv0Hy^wDR&$V$C3 zPhx)C!ja?AFq?C_ZM;BEx<@0Ycd>PCOG8LvIrhv@>sff73Byc5@vIn$n6oRni3{as z-nWy#zlHt^K;z}IYogFQMFNLSVy1?)d42LsQ(fzG0R=$@rD4QoXTJ}l^VuR{c$5J+ z!~Ljyv!|ycIV_MdXbv(BFy3O8*X;LCF;rEP#HFDQPU(iH$g?Ip!?+4yz)0>FQNpr; zvVHS?X2_*X1^4&jcVDeY$c?c1FZ|!u5NmK!o+P&BaE`V1%o4f~MXo7=_s6UO)r@R; z2VxVK9Xu+{ehTkWoL8jxwtDk6G(vV@h=IJYr=U$AgHyFm<`>fta{k9AN zo&h`Y6%Otb!>N<_?sKSud~oW<6JK`@)YnW6KPgQ8x&M%6rd`_#i!xQ(4|>-}uGr5| z1fSmHRRNKk`}^Y_Aqaug{U5WCO`1scHr@_Z0!UcDY1{sN*iFmYT zs%Q^jC1KenF13Hg6NnS%%DqUO@($SqjdW1zi_Kl);+5?o&uRhNZFJ1Ip8hl)rXa^d zEy_2NEM<@-H2YDDpofwBNZfb%`eB)R(oN8Wx2B9@@lpL8(NDVt_|iQVhkNc&yvBMD z9Q-LZ6qr@<5YJpWellRyDRBb$72uP; zRmvoOTmcq6wHh*r!7t8YxaS(I$MZ8|18_@{tMK$tA0hK1D9q6spc{s77yI~rY-Zh- z;P{5Q$|f`q=u9z4{k4W+g%V-DoZjU2Yd=6XLIABwVHF=Oq7gh&sVjDlP*q@}26G@J{#xqXBrFCXN#i;$t-4YWlOz zg!zvc8cTSymp$3Y1ddQ9sK1+d00_bQ>{iQ*Ee4q93YIr7Z3l$c7-y^fy2`g&(j+R6 zVaS12>DNBB#*xCO6XM&Z85Eq1JjRGH-~3O8sTEdqGK?Y?-5GDzY3Hw8YDBRpQUs4@lECv^8Rvi?|3P%r z3YCKcdl;YmhD>Zq0~5n^%RXH^pEwLI{B+t-$l|@TDuq;*zJv&Uobx&B2O-Otm{V7 zxnWz^i{?d?H;cEm!XfXe^2eXa1^R142E2=*2D6vXZ45Y86{(J^O;>+u?Su6hcYo=l z%BPuU+;5WUv)#J4*h^Pao0uR|q(6K1!sBQcC1PesYhOcN8$XFD!Xa{c8{?#F7VXJJ zi%;rA?bTHXU)MuVS3oOL{5Sc3-{Q=Uj%cUa#R>q40DT4Y{rFKxr9IEX3~BHC^#Ys) zJh?`z%Z!WD=>4>J;#~UDd(;KL^hx`vw<6kxqk%!(jAs}oKX ztPX#2gALVIm30zjsW30rC(*51m$Us1H++9AN_&5PVsntXYAQ(EU95=2;;v3l)j&TU za~M*htuWkxTWnvP(wxPd#(r~=WiT_LHe$nPqt#c12f9b=ZbkoF z@f2f70AmJh_hMCc&_5&sI3)qYn1j7_t#G!Vmy$%6Y-z6iwyQ?ZfmDaO-~KxjHZeWX z7T@*g3Q&w(YqWIHBW3<;eEpx=l;_mg*)-yywM=2)=AU^LcTXdC9*hVE-~2;Oh196c z%}lqK>L$YFso1~HLh_+s(|LI`s=NI4y7VZml;^=a#9!O9Ic9W>ez@nQv8j^S6`$n~ z<`w!fl4#Q;rdL#IOkO2rO7m$&@m`|%ZBX1v#w~%9!uJ+(aA0L4 z>Od<=61p-crdg=geELb6PIOH4MVG$noP9+TtTEUZ-iZS#Z`JNv;Fi%FR*YbBj%*^p z!t#Pv=rFQMHVBdE5D82&2c(WS1)vnG;iwha#Md@NS;bKHs2J-S7U~SPY2N%`wMq%e z{LBDPg69@T_;f8TWq#>7aOsrIG_3Dqhqk4%N02fA!_bRyE+lgO(N#H^t`kj4!@xxC zKsL9-t6a@z-EZU&(_OrlrUIpc(raqKv?g0n=9q)9nM2&N@ByQHJXYK(_em*48_Qn<~A&<4gRO<**Y{TL;{#Io?-J<|+|H zkut>$v5IxaC*pHUO@qYG7{L3$QD(!b^6TUB4hG6~-NRrqYEK`hMO&rECF@RLDPTR4 zI|Uys1=zt(f8Y({c9pNP5ah0tr?^M|4;NiJ-vakAMpF< z)PD3k1<-|LTa-L}aw4zHloKRhz<-;huUj{eORtP6Tp4`^YjLrEGQ<{D zUHt^cV)+yh4MqYI;p(VvIcCn|Yrtw704c=)qtQFIYV-P3z!n^(groMvufL?bVI$2% zClZJIBUddc#&-^Bxjxc6b6R5~GP!6>jk)a)C7V*;-jS8CYy)$Boe#b;80LU#4|8>A zO@OX$uT^7S(8t5oaFgVu)^`;%sCqmKL$+YRW(^- z+!B6EwYXdgf--Ii5$+_&3_>Ms&V}lP^b$|}+$;nCdGm}_-L&eLuLp;3%UTE2q*Lhq zndyI^^!l_6LZz==+p8fD<~+I+2m|xZaN-}wDI%%e?{;C(fgRP}{n~hgaXjz3eSG(# zp}1WM--eB1{NtBaQv}r1GX!@kl+5DhrIs-%6oqe_pe5(^TYW!3FZ}_Q?e`f50F~_Q zC?T0PxwG1)wM-VjB=oxucFu_E;~dkbXKH_V@G{$^gkCaerV_rvl}?1uoaFJn|K%=r zJjbI@zHW?H4BZz*I}5YA)CbfpVhYu-Mv`ms-kf`3v~w2bh=_2iCUKt zguh^~j3&<~ks0(VENs+y#*I-N<_8Ypm?&gxaWPq)f!xpgU`P=mRm{k&7ZHmK*s*1= z(fGd1Q21ZwZG4kEpS$n84)3DG6z};Bq#5vw%}9TZ`zTosbP8l}VH6)AzyUQZ=nnT$Leb{{ diff --git a/subcommands/skins/skins.go b/subcommands/skins/skins.go index 3a439dd..af56545 100644 --- a/subcommands/skins/skins.go +++ b/subcommands/skins/skins.go @@ -3,126 +3,16 @@ package skins import ( "context" "flag" - "fmt" - "os" - "path" - "strings" - "time" "github.com/bedrock-tool/bedrocktool/locale" "github.com/bedrock-tool/bedrocktool/ui/messages" "github.com/bedrock-tool/bedrocktool/utils" + "github.com/bedrock-tool/bedrocktool/utils/handlers" - "github.com/google/uuid" - "github.com/sandertv/gophertunnel/minecraft/protocol" - "github.com/sandertv/gophertunnel/minecraft/protocol/packet" - "github.com/sirupsen/logrus" + "github.com/sandertv/gophertunnel/minecraft" ) -type SkinMeta struct { - SkinID string - PlayFabID string - PremiumSkin bool - PersonaSkin bool - CapeID string - SkinColour string - ArmSize string - Trusted bool - PersonaPieces []protocol.PersonaPiece -} - type skinsSession struct { - PlayerNameFilter string - OnlyIfHasGeometry bool - ServerName string - Proxy *utils.ProxyContext - fpath string - - playerSkinPacks map[uuid.UUID]*SkinPack - playerNames map[uuid.UUID]string -} - -func NewSkinsSession(proxy *utils.ProxyContext, serverName, fpath string) *skinsSession { - return &skinsSession{ - ServerName: serverName, - Proxy: proxy, - fpath: fpath, - - playerSkinPacks: make(map[uuid.UUID]*SkinPack), - playerNames: make(map[uuid.UUID]string), - } -} - -func (s *skinsSession) AddPlayerSkin(playerID uuid.UUID, playerName string, skin *Skin) (added bool) { - p, ok := s.playerSkinPacks[playerID] - if !ok { - creating := fmt.Sprintf("Creating Skinpack for %s", playerName) - s.Proxy.SendPopup(creating) - logrus.Info(creating) - p = NewSkinPack(playerName, s.fpath) - s.playerSkinPacks[playerID] = p - } - if p.AddSkin(skin) { - if ok { - addedStr := fmt.Sprintf("Added a skin to %s", playerName) - s.Proxy.SendPopup(addedStr) - logrus.Info(addedStr) - } - added = true - } - if err := p.Save(path.Join(s.fpath, playerName), s.ServerName); err != nil { - logrus.Error(err) - } - return added -} - -func (s *skinsSession) AddSkin(playerName string, playerID uuid.UUID, playerSkin *protocol.Skin) (string, *Skin, bool) { - if playerName == "" { - playerName = s.playerNames[playerID] - if playerName == "" { - playerName = playerID.String() - } - } - if !strings.HasPrefix(playerName, s.PlayerNameFilter) { - return "", nil, false - } - s.playerNames[playerID] = playerName - - skin := &Skin{playerSkin} - if s.OnlyIfHasGeometry && !skin.HaveGeometry() { - return "", nil, false - } - wasAdded := s.AddPlayerSkin(playerID, playerName, skin) - - return playerName, skin, wasAdded -} - -type skinAdd struct { - PlayerName string - Skin *protocol.Skin -} - -func (s *skinsSession) ProcessPacket(pk packet.Packet) (out []skinAdd) { - switch pk := pk.(type) { - case *packet.PlayerList: - if pk.ActionType == 1 { // remove - return nil - } - for _, player := range pk.Entries { - playerName, skin, wasAdded := s.AddSkin(utils.CleanupName(player.Username), player.UUID, &player.Skin) - if wasAdded { - out = append(out, skinAdd{ - PlayerName: playerName, - Skin: skin.Skin, - }) - } - } - case *packet.AddPlayer: - if _, ok := s.playerNames[pk.UUID]; !ok { - s.playerNames[pk.UUID] = utils.CleanupName(pk.Username) - } - } - return out } type SkinCMD struct { @@ -148,41 +38,25 @@ func (c *SkinCMD) Execute(ctx context.Context, ui utils.UI) error { proxy, _ := utils.NewProxy() proxy.WithClient = !c.NoProxy - proxy.OnClientConnect = func(hasClient bool) { - ui.Message(messages.SetUIState(messages.UIStateConnecting)) - } - proxy.ConnectCB = func(err error) bool { - if err != nil { - return false - } - ui.Message(messages.SetUIState(messages.UIStateMain)) - logrus.Info(locale.Loc("ctrl_c_to_exit", nil)) - return true - } - - outPathBase := fmt.Sprintf("skins/%s", hostname) - os.MkdirAll(outPathBase, 0o755) - - s := NewSkinsSession(proxy, hostname, outPathBase) - - proxy.PacketCB = func(pk packet.Packet, toServer bool, _ time.Time) (packet.Packet, error) { - if !toServer { - for _, s := range s.ProcessPacket(pk) { - ui.Message(messages.NewSkin{ - PlayerName: s.PlayerName, - Skin: s.Skin, - }) - } - } - return pk, nil - } + proxy.AddHandler(handlers.NewSkinSaver(func(sa handlers.SkinAdd) { + ui.Message(messages.NewSkin{ + PlayerName: sa.PlayerName, + Skin: sa.Skin, + }) + })) + proxy.AddHandler(&utils.ProxyHandler{ + Name: "Skin CMD", + OnClientConnect: func(conn *minecraft.Conn) { + ui.Message(messages.SetUIState(messages.UIStateConnecting)) + }, + }) if proxy.WithClient { ui.Message(messages.SetUIState(messages.UIStateConnect)) } else { ui.Message(messages.SetUIState(messages.UIStateConnecting)) } - err = proxy.Run(ctx, address) + err = proxy.Run(ctx, address, hostname) return err } diff --git a/subcommands/world/world.go b/subcommands/world/world.go index 7a33e0f..74d9d57 100644 --- a/subcommands/world/world.go +++ b/subcommands/world/world.go @@ -172,37 +172,41 @@ func (c *WorldCMD) Execute(ctx context.Context, ui utils.UI) error { proxy.GameDataModifier = func(gd *minecraft.GameData) { gd.ClientSideGeneration = false } - proxy.ConnectCB = w.OnConnect - proxy.OnClientConnect = func(hasClient bool) { - w.gui.Message(messages.SetUIState(messages.UIStateConnecting)) - } - proxy.PacketCB = func(pk packet.Packet, toServer bool, _ time.Time) (packet.Packet, error) { - forward := true - if toServer { - // from client - pk = w.processItemPacketsClient(pk, &forward) - pk = w.processMapPacketsClient(pk, &forward) - } else { - // from server - switch pk := pk.(type) { - case *packet.ChunkRadiusUpdated: - w.serverState.ChunkRadius = int(pk.ChunkRadius) - pk.ChunkRadius = 80 + proxy.AddHandler(&utils.ProxyHandler{ + Name: "Worlds", + ConnectCB: w.OnConnect, + OnClientConnect: func(conn *minecraft.Conn) { + w.gui.Message(messages.SetUIState(messages.UIStateConnecting)) + }, + PacketCB: func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) { + forward := true + + if toServer { + // from client + pk = w.processItemPacketsClient(pk, &forward) + pk = w.processMapPacketsClient(pk, &forward) + } else { + // from server + switch pk := pk.(type) { + case *packet.ChunkRadiusUpdated: + w.serverState.ChunkRadius = int(pk.ChunkRadius) + pk.ChunkRadius = 80 + } + pk = w.processItemPacketsServer(pk) + pk = w.ProcessChunkPackets(pk) + pk = w.ProcessEntityPackets(pk) } - pk = w.processItemPacketsServer(pk) - pk = w.ProcessChunkPackets(pk) - pk = w.ProcessEntityPackets(pk) - } - if !forward { - return nil, nil - } - return pk, nil - } + if !forward { + return nil, nil + } + return pk, nil + }, + }) w.gui.Message(messages.SetUIState(messages.UIStateConnect)) - err = w.proxy.Run(ctx, serverAddress) + err = w.proxy.Run(ctx, serverAddress, hostname) if err != nil { return err } diff --git a/utils/handlers/capture.go b/utils/handlers/capture.go new file mode 100644 index 0000000..9d86b36 --- /dev/null +++ b/utils/handlers/capture.go @@ -0,0 +1,69 @@ +package handlers + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "net" + "os" + "sync" + "time" + + "github.com/bedrock-tool/bedrocktool/utils" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" +) + +var dumpLock sync.Mutex + +func dumpPacket(f io.WriteCloser, toServer bool, payload []byte) { + dumpLock.Lock() + defer dumpLock.Unlock() + f.Write([]byte{0xAA, 0xAA, 0xAA, 0xAA}) + packetSize := uint32(len(payload)) + binary.Write(f, binary.LittleEndian, packetSize) + binary.Write(f, binary.LittleEndian, toServer) + binary.Write(f, binary.LittleEndian, time.Now().UnixMilli()) + f.Write(payload) + f.Write([]byte{0xBB, 0xBB, 0xBB, 0xBB}) +} + +type PacketCapturer struct { + proxy *utils.ProxyContext + fio *os.File +} + +func (p *PacketCapturer) AddressAndName(address, hostname string) error { + os.Mkdir("captures", 0o775) + fio, err := os.Create(fmt.Sprintf("captures/%s-%s.pcap2", hostname, time.Now().Format("2006-01-02_15-04-05"))) + if err != nil { + return err + } + utils.WriteReplayHeader(fio) + p.fio = fio + return nil +} + +func (p *PacketCapturer) PacketFunc(header packet.Header, payload []byte, src, dst net.Addr) { + IsfromClient := utils.ClientAddr.String() == src.String() + + buf := bytes.NewBuffer(nil) + header.Write(buf) + buf.Write(payload) + dumpPacket(p.fio, IsfromClient, buf.Bytes()) +} + +func NewPacketCapturer() *utils.ProxyHandler { + p := &PacketCapturer{} + return &utils.ProxyHandler{ + Name: "Packet Capturer", + ProxyRef: func(pc *utils.ProxyContext) { + p.proxy = pc + }, + PacketFunc: p.PacketFunc, + AddressAndName: p.AddressAndName, + OnEnd: func() { + p.fio.Close() + }, + } +} diff --git a/utils/handlers/chat_log.go b/utils/handlers/chat_log.go new file mode 100644 index 0000000..3f485cc --- /dev/null +++ b/utils/handlers/chat_log.go @@ -0,0 +1,54 @@ +package handlers + +import ( + "fmt" + "os" + "time" + + "github.com/bedrock-tool/bedrocktool/utils" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sirupsen/logrus" +) + +type ChatLogger struct { + Verbose bool + fio *os.File +} + +func (c *ChatLogger) AddressAndName(address, hostname string) error { + filename := fmt.Sprintf("%s_%s_chat.log", hostname, time.Now().Format("2006-01-02_15-04-05_Z07")) + f, err := os.Create(filename) + if err != nil { + return err + } + c.fio = f + return nil +} + +func (c *ChatLogger) PacketCB(pk packet.Packet, toServer bool, t time.Time) (packet.Packet, error) { + if text, ok := pk.(*packet.Text); ok { + logLine := text.Message + if c.Verbose { + logLine += fmt.Sprintf(" (TextType: %d | XUID: %s | PlatformChatID: %s)", text.TextType, text.XUID, text.PlatformChatID) + } + c.fio.WriteString(fmt.Sprintf("[%s] ", t.Format(time.RFC3339))) + logrus.Info(logLine) + if toServer { + c.fio.WriteString("SENT: ") + } + c.fio.WriteString(logLine + "\n") + } + return pk, nil +} + +func NewChatLogger() *utils.ProxyHandler { + p := &ChatLogger{} + return &utils.ProxyHandler{ + Name: "Packet Capturer", + PacketCB: p.PacketCB, + AddressAndName: p.AddressAndName, + OnEnd: func() { + p.fio.Close() + }, + } +} diff --git a/utils/handlers/skins.go b/utils/handlers/skins.go new file mode 100644 index 0000000..6c6b591 --- /dev/null +++ b/utils/handlers/skins.go @@ -0,0 +1,127 @@ +package handlers + +import ( + "fmt" + "os" + "path" + "strings" + "time" + + "github.com/bedrock-tool/bedrocktool/utils" + "github.com/google/uuid" + "github.com/sandertv/gophertunnel/minecraft/protocol" + "github.com/sandertv/gophertunnel/minecraft/protocol/packet" + "github.com/sirupsen/logrus" +) + +type SkinSaver struct { + PlayerNameFilter string + OnlyIfHasGeometry bool + ServerName string + Proxy *utils.ProxyContext + fpath string + + playerSkinPacks map[uuid.UUID]*utils.SkinPack + playerNames map[uuid.UUID]string +} + +func (s *SkinSaver) AddPlayerSkin(playerID uuid.UUID, playerName string, skin *utils.Skin) (added bool) { + p, ok := s.playerSkinPacks[playerID] + if !ok { + creating := fmt.Sprintf("Creating Skinpack for %s", playerName) + s.Proxy.SendPopup(creating) + logrus.Info(creating) + p = utils.NewSkinPack(playerName, s.fpath) + s.playerSkinPacks[playerID] = p + } + if p.AddSkin(skin) { + if ok { + addedStr := fmt.Sprintf("Added a skin to %s", playerName) + s.Proxy.SendPopup(addedStr) + logrus.Info(addedStr) + } + added = true + } + if err := p.Save(path.Join(s.fpath, playerName), s.ServerName); err != nil { + logrus.Error(err) + } + return added +} + +func (s *SkinSaver) AddSkin(playerName string, playerID uuid.UUID, playerSkin *protocol.Skin) (string, *utils.Skin, bool) { + if playerName == "" { + playerName = s.playerNames[playerID] + if playerName == "" { + playerName = playerID.String() + } + } + if !strings.HasPrefix(playerName, s.PlayerNameFilter) { + return "", nil, false + } + s.playerNames[playerID] = playerName + + skin := &utils.Skin{playerSkin} + if s.OnlyIfHasGeometry && !skin.HaveGeometry() { + return "", nil, false + } + wasAdded := s.AddPlayerSkin(playerID, playerName, skin) + + return playerName, skin, wasAdded +} + +type SkinAdd struct { + PlayerName string + Skin *protocol.Skin +} + +func (s *SkinSaver) ProcessPacket(pk packet.Packet) (out []SkinAdd) { + switch pk := pk.(type) { + case *packet.PlayerList: + if pk.ActionType == 1 { // remove + return nil + } + for _, player := range pk.Entries { + playerName, skin, wasAdded := s.AddSkin(utils.CleanupName(player.Username), player.UUID, &player.Skin) + if wasAdded { + out = append(out, SkinAdd{ + PlayerName: playerName, + Skin: skin.Skin, + }) + } + } + case *packet.AddPlayer: + if _, ok := s.playerNames[pk.UUID]; !ok { + s.playerNames[pk.UUID] = utils.CleanupName(pk.Username) + } + } + return out +} + +func NewSkinSaver(skinCB func(SkinAdd)) *utils.ProxyHandler { + s := &SkinSaver{ + playerSkinPacks: make(map[uuid.UUID]*utils.SkinPack), + playerNames: make(map[uuid.UUID]string), + } + return &utils.ProxyHandler{ + Name: "Skin Saver", + ProxyRef: func(pc *utils.ProxyContext) { + s.Proxy = pc + }, + AddressAndName: func(address, hostname string) error { + outPathBase := fmt.Sprintf("skins/%s", hostname) + os.MkdirAll(outPathBase, 0o755) + s.fpath = outPathBase + return nil + }, + PacketCB: func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) { + if !toServer { + for _, s := range s.ProcessPacket(pk) { + if skinCB != nil { + skinCB(s) + } + } + } + return pk, nil + }, + } +} diff --git a/utils/packet_logger.go b/utils/packet_logger.go index a49a0da..c3461c5 100644 --- a/utils/packet_logger.go +++ b/utils/packet_logger.go @@ -183,10 +183,6 @@ var ClientAddr net.Addr var pool = packet.NewPool() func PacketLogger(header packet.Header, payload []byte, src, dst net.Addr) { - if header.PacketID == packet.IDRequestNetworkSettings { - ClientAddr = src - } - var pk packet.Packet if pkFunc, ok := pool[header.PacketID]; ok { pk = pkFunc() diff --git a/utils/proxy.go b/utils/proxy.go index bda6824..27f12d5 100644 --- a/utils/proxy.go +++ b/utils/proxy.go @@ -8,11 +8,13 @@ import ( "fmt" "net" "os" + "reflect" "strings" "sync" "time" "github.com/bedrock-tool/bedrocktool/locale" + "github.com/repeale/fp-go" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/login" @@ -42,16 +44,36 @@ func (p dummyProto) ConvertFromLatest(pk packet.Packet, _ *minecraft.Conn) []pac */ type ( - PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr) - PacketCallback func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) - ClientConnectCallback func(hasClient bool) - ConnectCallback func(err error) bool - IngameCommand struct { + PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr) + IngameCommand struct { Exec func(cmdline []string) bool Cmd protocol.Command } ) +type ProxyHandler struct { + Name string + ProxyRef func(*ProxyContext) + // + AddressAndName func(address, name string) error + + // called for every packet + PacketFunc func(header packet.Header, payload []byte, src, dst net.Addr) + + // called on every packet after login + PacketCB func(pk packet.Packet, toServer bool, timeReceived time.Time) (packet.Packet, error) + + // called after client connected + OnClientConnect func(conn *minecraft.Conn) + SecondaryClientCB func(conn *minecraft.Conn) + + // called after game started + ConnectCB func(err error) bool + + // called when the proxy stops + OnEnd func() +} + type ProxyContext struct { Server *minecraft.Conn Client *minecraft.Conn @@ -62,14 +84,8 @@ type ProxyContext struct { IgnoreDisconnect bool CustomClientData *login.ClientData - // called for every packet - PacketFunc PacketFunc - // called after client connected - OnClientConnect ClientConnectCallback - // called after game started - ConnectCB ConnectCallback - // called on every packet after login - PacketCB PacketCallback + handlers []*ProxyHandler + // called to change game data GameDataModifier func(*minecraft.GameData) } @@ -186,6 +202,10 @@ func (p *ProxyContext) SendPopup(text string) { }) } +func (p *ProxyContext) AddHandler(handler *ProxyHandler) { + p.handlers = append(p.handlers, handler) +} + func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, toServer bool, _ time.Time) (packet.Packet, error) { switch _pk := pk.(type) { case *packet.CommandRequest: @@ -209,7 +229,7 @@ func (p *ProxyContext) CommandHandlerPacketCB(pk packet.Packet, toServer bool, _ return pk, nil } -func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool, packetCBs []PacketCallback) error { +func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool) error { var c1, c2 *minecraft.Conn if toServer { c1 = p.Client @@ -229,10 +249,17 @@ func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool, packetCBs [ return err } - for _, packetCB := range packetCBs { - pk, err = packetCB(pk, toServer, time.Now()) - if err != nil { - return err + pkName := reflect.TypeOf(pk).String() + for _, handler := range p.handlers { + if handler.PacketCB != nil { + pk, err = handler.PacketCB(pk, toServer, time.Now()) + if err != nil { + return err + } + if pk == nil { + logrus.Tracef("Dropped Packet: %s", pkName) + break + } } } @@ -247,7 +274,16 @@ func (p *ProxyContext) proxyLoop(ctx context.Context, toServer bool, packetCBs [ } } -func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error) { +func (p *ProxyContext) Run(ctx context.Context, serverAddress, name string) (err error) { + for _, handler := range p.handlers { + if handler.AddressAndName != nil { + handler.AddressAndName(serverAddress, name) + } + if handler.ProxyRef != nil { + handler.ProxyRef(p) + } + } + if strings.HasPrefix(serverAddress, "PCAP!") { return createReplayConnection(ctx, serverAddress[5:], p) } @@ -279,9 +315,7 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error if err != nil { return err } - - go func() { - <-ctx.Done() + defer func() { if p.Client != nil { p.Listener.Disconnect(p.Client, DisconnectReason) } @@ -300,22 +334,40 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error cdp = &cd } - if p.OnClientConnect != nil { - p.OnClientConnect(p.WithClient) + for _, handler := range p.handlers { + if handler.OnClientConnect == nil { + continue + } + handler.OnClientConnect(p.Client) } if p.CustomClientData != nil { cdp = p.CustomClientData } - p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, p.PacketFunc) + p.Server, err = connectServer(ctx, serverAddress, cdp, p.AlwaysGetPacks, func(header packet.Header, payload []byte, src, dst net.Addr) { + for _, handler := range p.handlers { + if handler.PacketFunc == nil { + continue + } + handler.PacketFunc(header, payload, src, dst) + } + }) if err != nil { - if p.ConnectCB != nil { - if p.ConnectCB(err) { + for _, handler := range p.handlers { + if handler.ConnectCB == nil { + continue + } + ignore := handler.ConnectCB(err) + if ignore { err = nil + break } } - err = fmt.Errorf(locale.Loc("failed_to_connect", locale.Strmap{"Address": serverAddress, "Err": err})) + + if err != nil { + err = fmt.Errorf(locale.Loc("failed_to_connect", locale.Strmap{"Address": serverAddress, "Err": err})) + } return err } defer p.Server.Close() @@ -331,25 +383,27 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error return err } - if p.ConnectCB != nil { - if !p.ConnectCB(nil) { + for _, handler := range p.handlers { + if handler.ConnectCB == nil { + continue + } + if !handler.ConnectCB(nil) { return errors.New("Cancelled") } } + // append self to handlers for commands + p.handlers = append(p.handlers, &ProxyHandler{ + Name: "Commands", + PacketCB: p.CommandHandlerPacketCB, + }) + wg := sync.WaitGroup{} - - var cbs []PacketCallback - cbs = append(cbs, p.CommandHandlerPacketCB) - if p.PacketCB != nil { - cbs = append(cbs, p.PacketCB) - } - // server to client wg.Add(1) go func() { defer wg.Done() - if err := p.proxyLoop(ctx, false, cbs); err != nil { + if err := p.proxyLoop(ctx, false); err != nil { logrus.Error(err) return } @@ -360,13 +414,37 @@ func (p *ProxyContext) Run(ctx context.Context, serverAddress string) (err error wg.Add(1) go func() { defer wg.Done() - if err := p.proxyLoop(ctx, true, cbs); err != nil { + if err := p.proxyLoop(ctx, true); err != nil { logrus.Error(err) return } }() } + wantSecondary := fp.Filter(func(handler *ProxyHandler) bool { + return handler.SecondaryClientCB != nil + })(p.handlers) + + if len(wantSecondary) > 0 { + go func() { + c, err := p.Listener.Accept() + if err != nil { + logrus.Error(err) + return + } + + for _, handler := range wantSecondary { + go handler.SecondaryClientCB(c.(*minecraft.Conn)) + } + }() + } + wg.Wait() + + for _, handler := range p.handlers { + if handler.OnEnd != nil { + handler.OnEnd() + } + } return err } diff --git a/utils/replay.go b/utils/replay.go index 1d270b7..d73f57a 100644 --- a/utils/replay.go +++ b/utils/replay.go @@ -63,6 +63,13 @@ func createReplayConnection(ctx context.Context, filename string, proxy *ProxyCo f.Seek(-4, io.SeekCurrent) } + server, client := &net.UDPAddr{ + IP: net.IPv4(1, 1, 1, 1), + }, &net.UDPAddr{ + IP: net.IPv4(2, 2, 2, 2), + } + ClientAddr = client + proxy.Server = minecraft.NewConn() gameStarted := false @@ -123,13 +130,32 @@ func createReplayConnection(ctx context.Context, filename string, proxy *ProxyCo b := protocol.NewWriter(f, 0) pk.Marshal(b) + hdr := packet.Header{PacketID: pk.ID()} + + var src, dst net.Addr + if toServer { + src = client + dst = server + } else { + src = server + dst = client + } + if Options.Debug { - PacketLogger(packet.Header{PacketID: pk.ID()}, f.Bytes(), &net.UDPAddr{}, &net.UDPAddr{}) + PacketLogger(hdr, f.Bytes(), src, dst) + } + + for _, handler := range proxy.handlers { + if handler.PacketFunc != nil { + handler.PacketFunc(hdr, f.Bytes(), src, dst) + } } if gameStarted { - if proxy.PacketCB != nil { - proxy.PacketCB(pk, toServer, timeReceived) + for _, handler := range proxy.handlers { + if handler.PacketCB != nil { + handler.PacketCB(pk, toServer, timeReceived) + } } } else { switch pk := pk.(type) { @@ -164,11 +190,19 @@ func createReplayConnection(ctx context.Context, filename string, proxy *ProxyCo DisablePlayerInteractions: pk.DisablePlayerInteractions, }) gameStarted = true - if proxy.ConnectCB != nil { - proxy.ConnectCB(nil) + for _, handler := range proxy.handlers { + if handler.ConnectCB != nil { + handler.ConnectCB(nil) + } } } } } + + for _, handler := range proxy.handlers { + if handler.OnEnd != nil { + handler.OnEnd() + } + } } } diff --git a/subcommands/skins/skin.go b/utils/skin.go similarity index 99% rename from subcommands/skins/skin.go rename to utils/skin.go index 31688c4..327a580 100644 --- a/subcommands/skins/skin.go +++ b/utils/skin.go @@ -1,4 +1,4 @@ -package skins +package utils import ( "encoding/json" diff --git a/subcommands/skins/skinpack.go b/utils/skinpack.go similarity index 89% rename from subcommands/skins/skinpack.go rename to utils/skinpack.go index fb13cc2..ee60e4c 100644 --- a/subcommands/skins/skinpack.go +++ b/utils/skinpack.go @@ -1,4 +1,4 @@ -package skins +package utils import ( "encoding/json" @@ -6,12 +6,24 @@ import ( "os" "path" - "github.com/bedrock-tool/bedrocktool/utils" "github.com/google/uuid" + "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/resource" "github.com/sirupsen/logrus" ) +type SkinMeta struct { + SkinID string + PlayFabID string + PremiumSkin bool + PersonaSkin bool + CapeID string + SkinColour string + ArmSize string + Trusted bool + PersonaPieces []protocol.PersonaPiece +} + type _skinWithIndex struct { i int skin *Skin @@ -144,7 +156,7 @@ func (s *SkinPack) Save(fpath, serverName string) error { }, } - if err := utils.WriteManifest(&manifest, fpath); err != nil { + if err := WriteManifest(&manifest, fpath); err != nil { return err } } diff --git a/utils/utils.go b/utils/utils.go index fe6ec65..c6706c0 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -69,6 +69,10 @@ func connectServer(ctx context.Context, address string, ClientData *login.Client TokenSource: GetTokenSource(), ClientData: cd, PacketFunc: func(header packet.Header, payload []byte, src, dst net.Addr) { + if header.PacketID == packet.IDRequestNetworkSettings { + ClientAddr = src + } + if Options.Debug { PacketLogger(header, payload, src, dst) } @@ -85,7 +89,6 @@ func connectServer(ctx context.Context, address string, ClientData *login.Client } logrus.Debug(locale.Loc("connected", nil)) - ClientAddr = serverConn.LocalAddr() return serverConn, nil }