Refactor mclog #29

Merged
Sirherobrine23 merged 2 commits from refactor-mclog into main 2025-03-19 00:55:42 +00:00
21 changed files with 1458 additions and 1024 deletions

View File

@ -1,279 +0,0 @@
package bedrock
import (
"bufio"
"fmt"
"io"
"slices"
"strings"
"time"
"sirherobrine23.com.br/go-bds/go-bds/mclog"
"sirherobrine23.com.br/go-bds/go-bds/regex"
)
const (
PlayerActionDisconnect string = "disconnect" // Player disconnected from server
PlayerActionConnect string = "connect" // Player connect in to server
PlayerActionSpawn string = "spawn" // Player spawned in server and connected correct to server, only new server (1.16+)
)
var (
_ = mclog.RegisterNewParse(&BedrockLoger{})
MojangPlayerActions = map[string]*regex.Regexp{
// [2024-04-01 20:50:26:198 INFO] Player connected: Sirherobrine, xuid: 2535413418839840
// [2024-04-01 21:46:11:691 INFO] Player connected: nod dd, xuid:
// [2024-04-01 20:50:31:386 INFO] Player Spawned: Sirherobrine xuid: 2535413418839840
// [2024-04-01 21:46:16:637 INFO] Player Spawned: nod dd xuid: , pfid: c31902da495f4549
// [2022-08-30 20:56:55:231 INFO] Player disconnected: Sirherobrine, xuid: 2535413418839840
// [2024-04-01 21:46:33:199 INFO] Player disconnected: nod dd, xuid: , pfid: c31902da495f4549
// NO LOG FILE! - [2025-01-19 23:35:18 INFO] Player connected: 2535413418839840
// NO LOG FILE! - [2025-01-19 23:38:54 INFO] Player disconnected: 2535413418839840
//
// TimeAction = time.Time{}
// Action = disconnected|connected|Spawned
// Username = String
// Xuid = String
`v2`: regex.MustCompile(`(?m)^\[(?P<TimeAction>([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})):[0-9]{1,3} INFO\ Player (?P<Action>disconnected|connected|Spawned): (?P<Username>[0-9A-Za-z_\-\s]+), xuid:\s?(?P<Xuid>[0-9A-Za-z]+)?,?`),
}
MojangPort = map[string]*regex.Regexp{
// NO LOG FILE! - [2024-07-30 00:33:21 INFO] IPv4 supported, port: 19132
// NO LOG FILE! - [2024-07-30 00:33:21 INFO] IPv6 supported, port: 19133
// NO LOG FILE! - [2024-07-30 00:33:22 INFO] Listening on IPv6 port: 19133
// NO LOG FILE! - [2024-07-30 00:33:22 INFO] Listening on IPv4 port: 19132
// [2023-03-08 13:01:57 INFO] Listening on IPv4 port: 19132
`v3`: regex.MustCompile(`(?m)\[(?P<TimeAction>([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})) INFO\] Listening on IPv(?P<Protocol>4|6) port: (?P<Port>[0-9]{1,5})`),
// [2024-07-29 20:48:07:066 INFO] IPv4 supported, port: 19132: Used for gameplay and LAN discovery
// [2024-07-29 20:48:07:066 INFO] IPv6 supported, port: 19133: Used for gameplay
`v2`: regex.MustCompile(`(?m)^\[(?P<TimeAction>([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})):[0-9]{1,3} INFO\] IPv(?P<Protocol>4|6) supported, port: (?P<Port>[0-9]{1,5})`),
// [INFO] IPv4 supported, port: 19132
`v1`: regex.MustCompile(`(?m)^\[INFO\] IPv(?P<Protocol>4|6) supported, port: (?P<Port>[0-9]{1,5})`),
}
MojangStarter = map[string]*regex.Regexp{
// [2024-04-10 11:16:29:640 INFO] Server started.
`v2`: regex.MustCompile(`(?m)^\[(?P<TimeAction>([0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2})):[0-9]{1,3} INFO\] Server started\.`),
}
)
type BedrockLoger struct{}
func (BedrockLoger) String() string { return "mojang:bedrock" }
func (BedrockLoger) New() (mclog.ServerParse, error) { return &LogerParse{mclog.Insights{}}, nil }
type LogerParse struct {
local mclog.Insights
}
func (loger LogerParse) Insight() *mclog.Insights { return &loger.local }
func (loger *LogerParse) Detect(log io.ReadSeeker) error {
loger.local = mclog.Insights{
ID: "mojang/bedrock",
Name: "bedrock",
Type: "Server log",
Title: "bedrock",
Version: "unknown",
Analysis: map[mclog.LogLevel][]*mclog.InsightsAnalysis{},
}
logScan := bufio.NewScanner(log)
isValid := false
for logScan.Scan() {
text := logScan.Text()
if !strings.Contains(text, "]") {
continue
}
isValid = true
Analysis := &mclog.InsightsAnalysis{
Entry: mclog.AnalysisEntry{
Lines: []mclog.EntryLine{
{
Content: text,
Numbers: strings.Count(text, "") - len(strings.Split(text, " ")),
},
},
},
}
prefixSplit := strings.SplitN(text, "]", 2)
prefixSplit[0] = prefixSplit[0][strings.Index(prefixSplit[0], "[")+1:]
if prefixSplit[1] = strings.TrimSpace(prefixSplit[1]); prefixSplit[1] == "" {
continue // skip
}
Analysis.Entry.Prefix = prefixSplit[0]
Analysis.Message = prefixSplit[1]
Analysis.Value = prefixSplit[1]
logLevel, add, err := mclog.LogUnknown, false, error(nil)
if strings.HasSuffix(prefixSplit[0], "INFO") {
logLevel = mclog.LogInfo
prefixSplit[0] = prefixSplit[0][:len(prefixSplit[0])-5]
} else if strings.HasSuffix(prefixSplit[0], "ERROR") {
logLevel = mclog.LogProblem
prefixSplit[0] = prefixSplit[0][:len(prefixSplit[0])-6]
Analysis.Label = "Error"
add = true
} else {
return mclog.ErrSkipParse
}
// Date
if len(prefixSplit[0]) >= 19 {
if Analysis.Entry.EntryTime, err = time.ParseInLocation("2006-01-02 15:04:05", prefixSplit[0][:19], time.Local); err != nil {
return err
}
Analysis.Entry.EntryTime = Analysis.Entry.EntryTime.UTC() // Convert to UTC time
}
explodeString := strings.Fields(prefixSplit[1])
switch explodeString[0] {
case "Server":
if strings.Contains(text, "started") {
add = true
Analysis.Label = "Server started"
Analysis.Value = "started"
Analysis.Message = "Server started"
}
case "Version":
if len(explodeString) >= 2 {
add = true
version := explodeString[len(explodeString)-1]
loger.local.Version = version
Analysis.Value = version
Analysis.Label = "Bedrock version"
}
case "IPv6", "IPv4", "Listening":
if explodeString[len(explodeString)-2] == "port:" {
protoLocation := 0
if explodeString[0] == "Listening" {
protoLocation = len(explodeString) - 3
}
Analysis.Label = explodeString[protoLocation]
Analysis.Value = explodeString[len(explodeString)-1]
Analysis.Label = "Port"
add = true
}
case "Player":
if !slices.Contains([]string{"connected:", "Spawned:", "disconnected:"}, explodeString[1]) {
continue
}
add = true
Analysis.Label = explodeString[1][:len(explodeString[1])-1]
Analysis.Message = "player connection"
player := prefixSplit[1][strings.Index(prefixSplit[1], explodeString[1])+len(explodeString[1]):]
xuid := ""
if strings.Contains(player, "xuid:") {
xuid = player[strings.LastIndex(player, "xuid:")+5:]
player = player[:strings.LastIndex(player, "xuid:")-2]
if strings.Contains(xuid, ",") {
xuid = xuid[:strings.Index(player, ",")]
}
xuid = strings.TrimSpace(xuid)
player = strings.TrimSpace(player)
}
Analysis.Value = player
Analysis.External = PlayerConnection{
Player: player,
XUID: xuid,
Action: Analysis.Label,
}
}
if add {
Analysis.Counter = len(Analysis.Entry.Lines)
loger.local.Analysis[logLevel] = append(loger.local.Analysis[logLevel], Analysis)
}
}
if !isValid {
return mclog.ErrSkipParse
}
return nil
}
type PlayerConnection struct {
Player string `json:"player"` // Player username
XUID string `json:"xuid,omitempty"` // Player xuid
Action string `json:"action"` // Connection type
}
type Handlers struct {
Started *time.Time `json:"started"` // Server started date
Ports []uint16 `json:"ports"` // Server ports
Players []PlayerConnection `json:"players"` // Player connnections in to server
}
// Server avaible time to player connect
func (w *Handlers) ParseStarted(logline string) {
for _, reg := range MojangStarter {
if reg.MatchString(logline) {
var err error
matched := reg.FindAllGroups(logline)
w.Started = new(time.Time)
if timeStr, ok := matched["TimeAction"]; ok {
if *w.Started, err = time.ParseInLocation(`2006-01-02 15:04:05`, timeStr, time.Local); err == nil {
return
}
} else if timeStr, ok := matched["Time"]; ok {
if *w.Started, err = time.ParseInLocation(`2006-01-02 15:04:05`, timeStr, time.Local); err == nil {
return
}
}
*w.Started = time.Now()
return
}
}
}
// Player action
func (w *Handlers) ParsePlayer(logline string) {
for _, reg := range MojangPlayerActions {
if reg.MatchString(logline) {
ActionPlayer := reg.FindAllGroups(logline)
action := strings.ToLower(ActionPlayer["Action"])
if slices.Contains([]string{PlayerActionConnect, PlayerActionDisconnect, PlayerActionSpawn}, action) {
w.Players = append(w.Players, PlayerConnection{
Player: strings.TrimSpace(ActionPlayer["Username"]),
XUID: strings.TrimSpace(ActionPlayer["Xuid"]),
Action: action,
})
}
return
}
}
}
// Port listen
func (w *Handlers) ParsePort(logline string) {
for _, reg := range MojangPort {
if reg.MatchString(logline) {
infoPort := reg.FindAllGroups(logline)
var port uint16
if _, err := fmt.Sscan(infoPort["Port"], &port); err != nil {
return
}
// Check if port ared added to slice
if !slices.Contains(w.Ports, port) {
w.Ports = append(w.Ports, port)
}
return
}
}
}
// Parse log and register on handlers
func (w *Handlers) RegisterScan(log io.ReadCloser) {
defer log.Close()
logScan := bufio.NewScanner(log)
for logScan.Scan() {
logLine := logScan.Text()
go w.ParseStarted(logLine)
go w.ParsePlayer(logLine)
go w.ParsePort(logLine)
}
}

View File

@ -1,137 +0,0 @@
package java
import (
"bufio"
"fmt"
"io"
"slices"
"strings"
"time"
"sirherobrine23.com.br/go-bds/go-bds/mclog"
)
var _ = mclog.RegisterNewParse(NewLogParse{})
type NewLogParse struct{}
func (NewLogParse) String() string { return "mojang/java" }
func (NewLogParse) New() (mclog.ServerParse, error) { return &LogerParse{}, nil }
type LogerParse struct {
local mclog.Insights
}
func (loger LogerParse) Insight() *mclog.Insights { return &loger.local }
func (loger *LogerParse) Detect(log io.ReadSeeker) error {
loger.local = mclog.Insights{
ID: "mojang/java",
Name: "java",
Type: "Server log",
Title: "java",
Version: "unknown",
Analysis: map[mclog.LogLevel][]*mclog.InsightsAnalysis{},
}
scan := bufio.NewScanner(log)
isValid, Analysis, logLevel, err := false, (*mclog.InsightsAnalysis)(nil), mclog.LogUnknown, error(nil)
for scan.Scan() {
add := false
text := scan.Text()
if strings.HasPrefix(text, "Unpacking") || strings.HasPrefix(text, "Starting") {
continue
} else if !isValid {
if text[0] != '[' {
return mclog.ErrSkipParse
}
isValid = true
}
if text[0] != '[' {
if Analysis == nil {
continue
}
Analysis.Entry.Lines = append(Analysis.Entry.Lines, mclog.EntryLine{
Content: text,
Numbers: strings.Count(text, "") - len(strings.Split(text, " ")),
})
continue
}
Analysis = &mclog.InsightsAnalysis{
Value: text,
Entry: mclog.AnalysisEntry{
Lines: []mclog.EntryLine{
{
Content: text,
Numbers: strings.Count(text, "") - len(strings.Split(text, " ")),
},
},
},
}
prefixSplited := [3]string(strings.SplitAfterN(text, "]", 3))
prefixSplited[0] = strings.Replace(strings.Replace(strings.TrimSpace(prefixSplited[0][1:]), "[", "", 1), "]", "", 1)
prefixSplited[1] = strings.Replace(strings.Replace(strings.TrimSpace(prefixSplited[1][1:]), "[", "", 1), "]", "", 1)
prefixSplited[2] = strings.TrimSpace(prefixSplited[2][1:])
Analysis.Message = prefixSplited[2]
Analysis.Entry.Prefix = fmt.Sprintf("[%s] [%s]", prefixSplited[0], prefixSplited[1])
if len(strings.Split(prefixSplited[0], ":")) != 3 {
continue
}
if strings.HasSuffix(prefixSplited[1], "INFO") {
logLevel = mclog.LogInfo
} else if strings.HasSuffix(prefixSplited[1], "WARN") {
logLevel = mclog.LogWarn
Analysis.Label = "Error"
add = true
}
if Analysis.Entry.EntryTime, err = time.ParseInLocation(time.TimeOnly, prefixSplited[0], time.Local); err != nil {
return err
}
Analysis.Entry.EntryTime = Analysis.Entry.EntryTime.UTC()
contentExplode := strings.Fields(prefixSplited[2])
switch contentExplode[0] {
case "RCON":
if contentExplode[len(contentExplode)-2] == "on" {
add = true
Analysis.Label = "RCON"
Analysis.Value = contentExplode[len(contentExplode)-1]
}
case "Starting":
if len(contentExplode) <= 3 {
continue
}
switch contentExplode[len(contentExplode)-2] {
case "version":
add = true
version := contentExplode[len(contentExplode)-1]
loger.local.Version = version
Analysis.Value = version
Analysis.Label = "Java version"
case "on":
add = true
Analysis.Label = "Port"
Analysis.Value = contentExplode[len(contentExplode)-1][2:]
}
default:
switch contentExplode[len(contentExplode)-1] {
case "game":
if slices.Contains([]string{"left", "joined"}, contentExplode[len(contentExplode)-3]) {
add = true
Analysis.Message = "Player status"
Analysis.Label = contentExplode[len(contentExplode)-3]
Analysis.Value = prefixSplited[2][:strings.LastIndex(prefixSplited[2], contentExplode[len(contentExplode)-3])-1]
}
}
}
if add {
Analysis.Counter = len(Analysis.Entry.Lines)
loger.local.Analysis[logLevel] = append(loger.local.Analysis[logLevel], Analysis)
}
}
return nil
}

View File

@ -0,0 +1,35 @@
NO LOG FILE! - setting up server logging...
[2022-11-16 19:34:11:606 INFO] Starting Server
[2022-11-16 19:34:11:606 INFO] Version 1.19.41.01
[2022-11-16 19:34:11:606 INFO] Session ID d914c8d5-2ad1-46fd-b213-e76ed4e6caca
[2022-11-16 19:34:11:606 INFO] Level Name: Sirherobrine23
[2022-11-16 19:34:11:609 INFO] Game mode: 0 Survival
[2022-11-16 19:34:11:609 INFO] Difficulty: 2 NORMAL
[2022-11-16 19:34:11:729 INFO] opening worlds/Sirherobrine23/db
[2022-11-16 19:34:12:336 INFO] IPv4 supported, port: 19132
[2022-11-16 19:34:12:337 INFO] IPv6 supported, port: 19133
[2022-11-16 19:34:13:002 INFO] Server started.
[2022-11-16 19:34:13:008 INFO] Please note that LAN discovery will not function for this server.
[2022-11-16 19:34:13:008 INFO] Server IP must be specified in Servers tab in game.
[2022-11-16 19:34:13:052 INFO] ================ TELEMETRY MESSAGE ===================
[2022-11-16 19:34:13:052 INFO] Server Telemetry is currently not enabled.
[2022-11-16 19:34:13:052 INFO] Enabling this telemetry helps us improve the game.
[2022-11-16 19:34:13:052 INFO]
[2022-11-16 19:34:13:052 INFO] To enable this feature, add the line 'emit-server-telemetry=true'
[2022-11-16 19:34:13:052 INFO] to the server.properties file in the handheld/src-server directory
[2022-11-16 19:34:13:052 INFO] ======================================================
[2022-11-16 19:55:44:503 INFO] Player connected: Sirherobrine, xuid: 2535413418839840
[2022-11-16 19:55:49:400 INFO] Player Spawned: Sirherobrine xuid: 2535413418839840
[2022-11-16 19:56:59:378 INFO] Player disconnected: Sirherobrine, xuid: 2535413418839840
[2022-11-16 20:01:48:937 INFO] Running AutoCompaction...
[2022-11-16 20:07:48:959 INFO] Running AutoCompaction...
[2022-11-19 19:23:22:600 INFO] Running AutoCompaction...
[2022-11-19 19:29:22:601 INFO] Running AutoCompaction...
[2022-11-19 19:35:22:602 INFO] Running AutoCompaction...
[2022-11-19 19:41:23:746 INFO] Running AutoCompaction...
[2022-11-19 19:47:23:746 INFO] Running AutoCompaction...
[2022-11-19 19:53:23:747 INFO] Running AutoCompaction...
[2022-11-19 19:59:23:748 INFO] Running AutoCompaction...
[2022-11-19 20:05:24:904 INFO] Running AutoCompaction...
[2022-11-19 20:07:15:776 INFO] Player connected: Sirherobrine, xuid: 2535413418839840
[2022-11-19 20:07:30:911 INFO] Player Spawned: Sirherobrine xuid: 2535413418839840

View File

@ -0,0 +1,28 @@
NO LOG FILE! - setting up server logging...
[2025-03-02 18:47:06:641 INFO] Starting Server
[2025-03-02 18:47:06:641 INFO] Version: 1.21.70-beta25
[2025-03-02 18:47:06:641 INFO] Session ID: 5780f638-3db3-41b8-8ec8-7fa0fe39c0ca
[2025-03-02 18:47:06:641 INFO] Build ID: 31345670
[2025-03-02 18:47:06:641 INFO] Branch: r/21_u7
[2025-03-02 18:47:06:641 INFO] Commit ID: 7ca47dbf5277ac770e27cf3b41bb3e6d0ca27afa
[2025-03-02 18:47:06:641 INFO] Configuration: Publish
[2025-03-02 18:47:06:641 INFO] Level Name: Bedrock level
[2025-03-02 18:47:06:642 INFO] No CDN config file found for dedicated server
[2025-03-02 18:47:06:642 INFO] Game mode: 0 Survival
[2025-03-02 18:47:06:642 INFO] Difficulty: 1 EASY
[2025-03-02 18:47:06:644 INFO] Content logging to console is enabled.
[2025-03-02 18:47:07:160 INFO] Opening level 'worlds/Bedrock level/db'
[2025-03-02 18:47:07:175 INFO] [SERVER] Pack Stack - None
[2025-03-02 18:47:08:045 INFO] IPv4 supported, port: 19132: Used for gameplay and LAN discovery
[2025-03-02 18:47:08:045 INFO] IPv6 supported, port: 19133: Used for gameplay
[2025-03-02 18:47:08:083 INFO] Server started.
[2025-03-02 18:47:08:083 INFO] ================ TELEMETRY MESSAGE ===================
[2025-03-02 18:47:08:083 INFO] Server Telemetry is currently not enabled.
[2025-03-02 18:47:08:083 INFO] Enabling this telemetry helps us improve the game.
[2025-03-02 18:47:08:083 INFO]
[2025-03-02 18:47:08:083 INFO] To enable this feature, add the line 'emit-server-telemetry=true'
[2025-03-02 18:47:08:083 INFO] to the server.properties file in the handheld/src-server directory
[2025-03-02 18:47:08:083 INFO] ======================================================
[2025-03-02 18:47:15:702 INFO] Server stop requested.
[2025-03-02 18:47:15:704 INFO] Stopping server...
Quit correctly

16
logs/bedrock/1.6.1.0.txt Normal file
View File

@ -0,0 +1,16 @@
NO LOG FILE! - setting up server logging...
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Starting Server
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Version 1.6.1.0
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Level Name: Bedrock level
NO LOG FILE! - [2025-01-19 23:35:13 ERROR] Error opening whitelist file: whitelist.json
NO LOG FILE! - [2025-01-19 23:35:13 ERROR] Error opening ops file: ops.json
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Game mode: 0 Survival
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Difficulty: 1 EASY
NO LOG FILE! - [2025-01-19 23:35:13 INFO] IPv4 supported, port: 19132
NO LOG FILE! - [2025-01-19 23:35:13 INFO] IPv6 supported, port: 19133
NO LOG FILE! - [2025-01-19 23:35:14 INFO] Listening on IPv6 port: 19133
NO LOG FILE! - [2025-01-19 23:35:14 INFO] Listening on IPv4 port: 19132
NO LOG FILE! - [2025-01-19 23:35:18 INFO] Player connected: 2535413418839840
NO LOG FILE! - [2025-01-19 23:38:54 INFO] Player disconnected: 2535413418839840
NO LOG FILE! - [2025-01-19 23:40:13 INFO] Player connected:
NO LOG FILE! - [2025-01-19 23:40:54 INFO] Player disconnected:

221
logs/bedrock/bedrock.go Normal file
View File

@ -0,0 +1,221 @@
package bedrock
import (
"bufio"
"io"
"net/netip"
"slices"
"strconv"
"strings"
"time"
"sirherobrine23.com.br/go-bds/go-bds/logs"
"sirherobrine23.com.br/go-bds/go-bds/utils/slice"
)
var (
_ = logs.RegisterParse[*BedrockParse]("mojang/bedrock")
_ logs.Log = &BedrockParse{}
_ logs.Player = &BedrockPlayer{}
)
type BedrockPlayer struct {
Username string `json:"player"` // Player username
Actioned logs.Action `json:"action"` // Action type
Timed time.Time `json:"time"` // Action time
PlayerXUID int64 `json:"xuid"` // Player xuid
PFID string `json:"pfid"`
}
func (player BedrockPlayer) Name() string { return player.Username }
func (player BedrockPlayer) Action() logs.Action { return player.Actioned }
func (player BedrockPlayer) Time() time.Time { return player.Timed }
func (player BedrockPlayer) XUID() int64 { return player.PlayerXUID }
type BedrockParse struct {
LastCompaction time.Time `json:"last_world_compaction"` // Last world size reduce
SessionID string `json:"session"` // Server session
CommitID string `json:"commit"` // commit hash
Branch string `json:"branch"` // Server Branch build
ServerPlaform *logs.Server `json:"info"` // Basic server info
Players map[string][]logs.Player `json:"players"` // Players
Errs []error `json:"errors"`
Warngs []error `json:"warnings"`
err error
}
func (bedrock BedrockParse) Server() *logs.Server { return bedrock.ServerPlaform }
func (bedrock BedrockParse) Errors() []error { return bedrock.Errs }
func (bedrock BedrockParse) Warnings() []error { return bedrock.Warngs }
func (bedrock BedrockParse) GetPlayer(name string) (player []logs.Player, ok bool) {
player, ok = bedrock.Players[name]
return
}
func (bedrock *BedrockParse) ParseTime(current time.Time, log io.Reader) error {
return bedrock.Parse(log)
}
func (bedrock *BedrockParse) Parse(log io.Reader) error {
bedrock.ServerPlaform = &logs.Server{Platform: "mojang/bedrock", Ports: []*logs.Port{}} // Init info
bedrock.Errs, bedrock.Warngs = []error{}, []error{}
bedrock.Players = map[string][]logs.Player{}
scanner := bufio.NewScanner(log)
for scanner.Scan() {
text := scanner.Text()
text = strings.TrimPrefix(text, "NO LOG FILE! - ")
if strings.HasPrefix(text, "setting up server") || strings.HasPrefix(text, "Quit correctly") || text == "" {
continue
} else if !(strings.Contains(text, "]")) {
return logs.ErrSkipPlatform
}
prefixEnd := strings.Index(text, "]")
prefix := strings.TrimSpace(strings.TrimSuffix(text[strings.Index(text, "[")+1:prefixEnd], "INFO"))
line := strings.TrimSpace(text[prefixEnd+1:])
if line == "" {
continue // skip
} else if len(prefix) < 19 {
return logs.ErrSkipPlatform
}
err := error(nil)
if strings.HasSuffix(prefix, "ERROR") || strings.HasSuffix(prefix, "WARN") {
bedrock.Errs = append(bedrock.Errs, &logs.ErrorReference{LogLevel: 1, FistLine: line})
continue
}
// Date
if strings.Count(prefix, ":") == 3 {
lastColonIndex := strings.LastIndex(prefix, ":")
prefix = prefix[:lastColonIndex] + "." + prefix[lastColonIndex+1:]
}
EntryTime, err := time.Parse("2006-01-02 15:04:05.999", prefix)
if err != nil {
if EntryTime, err = time.Parse("2006-01-02 15:04:05", prefix[:19]); err != nil {
return err
}
}
EntryTime = EntryTime.UTC() // Convert to UTC time
explodeString := slice.Slice[string](strings.Fields(line))
switch explodeString.At(0) {
case "Server":
if strings.Contains(text, "started") {
bedrock.ServerPlaform.Started = EntryTime
}
case "Session":
if explodeString.At(1) == "ID" {
bedrock.SessionID = explodeString.At(2)
}
case "Branch:":
bedrock.Branch = explodeString.At(1)
case "Commit":
bedrock.CommitID = explodeString.At(2)
case "Running":
if strings.HasPrefix(explodeString.At(1), "AutoCompaction") {
bedrock.LastCompaction = EntryTime
}
case "Version", "Version:":
if len(explodeString) >= 2 {
bedrock.ServerPlaform.Version = explodeString.At(-1)
}
case "IPv6", "IPv4", "Listening":
if explodeString.At(-2) == "port:" {
protoLocation := 0
if explodeString.At(0) == "Listening" {
protoLocation = len(explodeString) - 3
}
port, err := strconv.ParseInt(explodeString.At(-1), 10, 16)
if err != nil {
return err
}
addr := netip.AddrPortFrom(netip.IPv4Unspecified(), uint16(port))
if explodeString[protoLocation] == "IPv6" {
addr = netip.AddrPortFrom(netip.IPv6Unspecified(), uint16(port))
}
bedrock.ServerPlaform.Ports = append(bedrock.ServerPlaform.Ports, &logs.Port{
AddrPort: addr,
From: "server",
})
}
case "Player":
// Player connected:
// Player disconnected:
// Player connected: 2535413418839840
// Player disconnected: 2535413418839840
// Player connected: Sirherobrine, xuid: 2535413418839840
// Player Spawned: Sirherobrine xuid: 2535413418839840
// Player disconnected: Sirherobrine, xuid: 2535413418839840
// Player Connected: nod dd, xuid: , pfid: c31902da495f4549
// Player Spawned: nod dd xuid: , pfid: c31902da495f4549
// Player disconnected: nod dd, xuid: , pfid: c31902da495f4549
if !slices.Contains([]string{"connected:", "spawned:", "disconnected:"}, strings.ToLower(explodeString.At(1))) {
continue
}
var player, xuid, pfid string
if player = strings.TrimSpace(line[strings.Index(line, explodeString.At(1))+len(explodeString.At(1)):]); player == "" {
// Old versions of Minecraft Bedrock Server (before 1.6.0)
// did not return the names of those who were not logged in
continue
}
if strings.Contains(player, ", xuid:") {
xuidd := strings.SplitN(player, ", xuid:", 2)
player = strings.TrimSpace(xuidd[0])
xuid = strings.TrimSpace(xuidd[1])
if strings.Contains(xuid, ", pfid:") {
pdis := strings.SplitN(xuid, ", pfid:", 2)
xuid = strings.TrimSpace(pdis[0])
pfid = strings.TrimSpace(pdis[1])
}
}
if strings.Contains(player, "xuid:") {
xuid = player[strings.LastIndex(player, "xuid:")+5:]
player = player[:strings.LastIndex(player, "xuid:")-1]
if player[len(player)-1] == ',' {
player = player[:len(player)-1]
}
if strings.Contains(xuid, ",") {
xuid = xuid[:strings.Index(player, ",")]
}
xuid = strings.TrimSpace(xuid)
player = strings.TrimSpace(player)
}
level := logs.Action(0)
switch strings.ToLower(explodeString.At(1)) {
case "disconnected:":
level = logs.Disconnect
case "connected:":
level = logs.Connect
case "spawned:":
level = logs.Spawned
}
if _, ok := bedrock.Players[player]; !ok {
bedrock.Players[player] = []logs.Player{}
}
xuidID, _ := strconv.ParseInt(xuid, 10, 64) // Convert xuid string to XUID int
bedrock.Players[player] = append(bedrock.Players[player], BedrockPlayer{
Username: player,
Actioned: level,
Timed: EntryTime,
PlayerXUID: xuidID,
PFID: pfid,
})
}
}
return scanner.Err()
}

View File

@ -0,0 +1,44 @@
package bedrock
import (
_ "embed"
"strings"
"encoding/json"
"testing"
)
var (
//go:embed 1.19.41.01.txt
StaticLogFileBedrock1 string
//go:embed 1.6.1.0.txt
StaticLogFileBedrock2 string
//go:embed 1.21.70-beta25.txt
StaticLogFileBedrock3 string
)
func testPrintLog(t *testing.T, textPrint string, log *BedrockParse) {
d, _ := json.MarshalIndent(log, "", " ")
t.Logf(textPrint, string(d))
}
func TestDetectAndParseBedrock(t *testing.T) {
parsedLog, err := &BedrockParse{}, error(nil)
if err = parsedLog.Parse(strings.NewReader(StaticLogFileBedrock1)); err != nil {
t.Errorf("Cannot parse bedrock log 1: %s", err)
return
}
testPrintLog(t, "Parsed log bedrock Static 1:\n%s", parsedLog)
if err = parsedLog.Parse(strings.NewReader(StaticLogFileBedrock2)); err != nil {
t.Errorf("Cannot parse bedrock log 2: %s", err)
return
}
testPrintLog(t, "Parsed log bedrock Static 2:\n%s", parsedLog)
if err = parsedLog.Parse(strings.NewReader(StaticLogFileBedrock3)); err != nil {
t.Errorf("Cannot parse bedrock log 3: %s", err)
return
}
testPrintLog(t, "Parsed log bedrock Static 3:\n%s", parsedLog)
}

104
logs/java/1.21.4.txt Normal file
View File

@ -0,0 +1,104 @@
Unpacking 1.21.4/server-1.21.4.jar (versions:1.21.4) to versions/1.21.4/server-1.21.4.jar
Unpacking com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar (libraries:com.fasterxml.jackson.core:jackson-annotations:2.13.4) to libraries/com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar
Unpacking com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar (libraries:com.fasterxml.jackson.core:jackson-core:2.13.4) to libraries/com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar
Unpacking com/fasterxml/jackson/core/jackson-databind/2.13.4.2/jackson-databind-2.13.4.2.jar (libraries:com.fasterxml.jackson.core:jackson-databind:2.13.4.2) to libraries/com/fasterxml/jackson/core/jackson-databind/2.13.4.2/jackson-databind-2.13.4.2.jar
Unpacking com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar (libraries:com.github.oshi:oshi-core:6.6.5) to libraries/com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar
Unpacking com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar (libraries:com.github.stephenc.jcip:jcip-annotations:1.0-1) to libraries/com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar
Unpacking com/google/code/gson/gson/2.11.0/gson-2.11.0.jar (libraries:com.google.code.gson:gson:2.11.0) to libraries/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar
Unpacking com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar (libraries:com.google.guava:failureaccess:1.0.2) to libraries/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar
Unpacking com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar (libraries:com.google.guava:guava:33.3.1-jre) to libraries/com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar
Unpacking com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar (libraries:com.microsoft.azure:msal4j:1.17.2) to libraries/com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar
Unpacking com/mojang/authlib/6.0.57/authlib-6.0.57.jar (libraries:com.mojang:authlib:6.0.57) to libraries/com/mojang/authlib/6.0.57/authlib-6.0.57.jar
Unpacking com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar (libraries:com.mojang:brigadier:1.3.10) to libraries/com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar
Unpacking com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar (libraries:com.mojang:datafixerupper:8.0.16) to libraries/com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar
Unpacking com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar (libraries:com.mojang:jtracy:1.0.29) to libraries/com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar
Unpacking com/mojang/logging/1.5.10/logging-1.5.10.jar (libraries:com.mojang:logging:1.5.10) to libraries/com/mojang/logging/1.5.10/logging-1.5.10.jar
Unpacking com/nimbusds/content-type/2.3/content-type-2.3.jar (libraries:com.nimbusds:content-type:2.3) to libraries/com/nimbusds/content-type/2.3/content-type-2.3.jar
Unpacking com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar (libraries:com.nimbusds:lang-tag:1.7) to libraries/com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar
Unpacking com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar (libraries:com.nimbusds:nimbus-jose-jwt:9.40) to libraries/com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar
Unpacking com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar (libraries:com.nimbusds:oauth2-oidc-sdk:11.18) to libraries/com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar
Unpacking commons-io/commons-io/2.17.0/commons-io-2.17.0.jar (libraries:commons-io:commons-io:2.17.0) to libraries/commons-io/commons-io/2.17.0/commons-io-2.17.0.jar
Unpacking io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar (libraries:io.netty:netty-buffer:4.1.115.Final) to libraries/io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar
Unpacking io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar (libraries:io.netty:netty-codec:4.1.115.Final) to libraries/io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar
Unpacking io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar (libraries:io.netty:netty-common:4.1.115.Final) to libraries/io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar
Unpacking io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar (libraries:io.netty:netty-handler:4.1.115.Final) to libraries/io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar
Unpacking io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar (libraries:io.netty:netty-resolver:4.1.115.Final) to libraries/io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar
Unpacking io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar (libraries:io.netty:netty-transport:4.1.115.Final) to libraries/io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar
Unpacking io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar (libraries:io.netty:netty-transport-classes-epoll:4.1.115.Final) to libraries/io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar
Unpacking io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar (libraries:io.netty:netty-transport-native-epoll:4.1.115.Final:linux-x86_64) to libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar
Unpacking io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar (libraries:io.netty:netty-transport-native-epoll:4.1.115.Final:linux-aarch_64) to libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar
Unpacking io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar (libraries:io.netty:netty-transport-native-unix-common:4.1.115.Final) to libraries/io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar
Unpacking it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar (libraries:it.unimi.dsi:fastutil:8.5.15) to libraries/it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar
Unpacking net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar (libraries:net.java.dev.jna:jna:5.15.0) to libraries/net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar
Unpacking net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar (libraries:net.java.dev.jna:jna-platform:5.15.0) to libraries/net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar
Unpacking net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar (libraries:net.minidev:accessors-smart:2.5.1) to libraries/net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar
Unpacking net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar (libraries:net.minidev:json-smart:2.5.1) to libraries/net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar
Unpacking net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar (libraries:net.sf.jopt-simple:jopt-simple:5.0.4) to libraries/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar
Unpacking org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar (libraries:org.apache.commons:commons-lang3:3.17.0) to libraries/org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar
Unpacking org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar (libraries:org.apache.logging.log4j:log4j-api:2.24.1) to libraries/org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar
Unpacking org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar (libraries:org.apache.logging.log4j:log4j-core:2.24.1) to libraries/org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar
Unpacking org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar (libraries:org.apache.logging.log4j:log4j-slf4j2-impl:2.24.1) to libraries/org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar
Unpacking org/joml/joml/1.10.8/joml-1.10.8.jar (libraries:org.joml:joml:1.10.8) to libraries/org/joml/joml/1.10.8/joml-1.10.8.jar
Unpacking org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar (libraries:org.lz4:lz4-java:1.8.0) to libraries/org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar
Unpacking org/ow2/asm/asm/9.6/asm-9.6.jar (libraries:org.ow2.asm:asm:9.6) to libraries/org/ow2/asm/asm/9.6/asm-9.6.jar
Unpacking org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar (libraries:org.slf4j:slf4j-api:2.0.16) to libraries/org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar
Starting net.minecraft.server.Main
[21:40:55] [ServerMain/INFO]: Environment: Environment[sessionHost=https://sessionserver.mojang.com, servicesHost=https://api.minecraftservices.com, name=PROD]
[21:40:59] [ServerMain/INFO]: No existing world data, creating new world
[21:41:01] [ServerMain/INFO]: Loaded 1370 recipes
[21:41:01] [ServerMain/INFO]: Loaded 1481 advancements
[21:41:01] [Server thread/INFO]: Starting minecraft server version 1.21.4
[21:41:01] [Server thread/INFO]: Loading properties
[21:41:01] [Server thread/INFO]: Default game type: SURVIVAL
[21:41:01] [Server thread/INFO]: Generating keypair
[21:41:01] [Server thread/INFO]: Starting Minecraft server on *:25565
[21:41:02] [Server thread/INFO]: Using epoll channel type
[21:41:02] [Server thread/INFO]: Preparing level "world"
[21:41:22] [Server thread/INFO]: Preparing start region for dimension minecraft:overworld
[21:41:23] [Worker-Main-2/INFO]: Preparing spawn area: 2%
[21:41:23] [Worker-Main-2/INFO]: Preparing spawn area: 2%
[21:41:24] [Worker-Main-2/INFO]: Preparing spawn area: 2%
[21:41:24] [Worker-Main-3/INFO]: Preparing spawn area: 2%
[21:41:24] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:25] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:25] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:26] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:26] [Worker-Main-3/INFO]: Preparing spawn area: 2%
[21:41:27] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:27] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:28] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:28] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:29] [Worker-Main-3/INFO]: Preparing spawn area: 8%
[21:41:30] [Worker-Main-2/INFO]: Preparing spawn area: 18%
[21:41:30] [Worker-Main-2/INFO]: Preparing spawn area: 18%
[21:41:30] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[21:41:31] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[21:41:31] [Worker-Main-3/INFO]: Preparing spawn area: 18%
[21:41:32] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[21:41:32] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[21:41:33] [Worker-Main-3/INFO]: Preparing spawn area: 18%
[21:41:33] [Worker-Main-3/INFO]: Preparing spawn area: 18%
[21:41:34] [Worker-Main-3/INFO]: Preparing spawn area: 18%
[21:41:34] [Worker-Main-3/INFO]: Preparing spawn area: 20%
[21:41:35] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:35] [Worker-Main-1/INFO]: Preparing spawn area: 51%
[21:41:36] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:36] [Worker-Main-2/INFO]: Preparing spawn area: 51%
[21:41:37] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:37] [Worker-Main-1/INFO]: Preparing spawn area: 51%
[21:41:38] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:38] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:39] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:39] [Worker-Main-2/INFO]: Preparing spawn area: 51%
[21:41:40] [Server thread/INFO]: Time elapsed: 17596 ms
[21:41:40] [Server thread/INFO]: Done (38.008s)! For help, type "help"
[21:41:40] [Server thread/INFO]: Starting remote control listener
[21:41:40] [Server thread/INFO]: Thread RCON Listener started
[21:41:40] [Server thread/INFO]: RCON running on 0.0.0.0:25575
[21:42:17] [User Authenticator #1/INFO]: UUID of player Sirherobrine23 is 0dc9df8f-9f5a-45d8-8848-9262a4357ae0
[21:42:20] [Server thread/INFO]: Sirherobrine23[/[0:0:0:0:0:0:0:1]:54662] logged in with entity id 41 at (37.5, 76.0, -57.5)
[21:42:20] [Server thread/INFO]: Sirherobrine23 joined the game
[21:43:42] [Server thread/WARN]: Sirherobrine23 moved too quickly! 9.663497831602484,3.176759275064242,0.8707508869438136
[21:43:42] [Server thread/WARN]: Can't keep up! Is the server overloaded? Running 4510ms or 90 ticks behind
[21:45:18] [Server thread/INFO]: Sirherobrine23 lost connection: Disconnected
[21:45:18] [Server thread/INFO]: Sirherobrine23 left the game

68
logs/java/1.7.10.txt Normal file
View File

@ -0,0 +1,68 @@
[Log4jPatcher] [INFO] Transforming org/apache/logging/log4j/core/lookup/JndiLookup
[Log4jPatcher] [INFO] Transforming org/apache/logging/log4j/core/pattern/MessagePatternConverter
[Log4jPatcher] [WARN] Unable to find noLookups:Z field in org/apache/logging/log4j/core/pattern/MessagePatternConverter
Jan 19, 2025 6:36:38 PM io.netty.util.internal.PlatformDependent <clinit>
INFO: Your platform does not provide complete low-level API for accessing direct buffers reliably. Unless explicitly requested, heap buffer will always be preferred to avoid potential system unstability.
[18:36:38] [Server thread/INFO]: Starting minecraft server version 1.7.10
[18:36:38] [Server thread/INFO]: Loading properties
[18:36:38] [Server thread/INFO]: Default game type: SURVIVAL
[18:36:38] [Server thread/INFO]: Generating keypair
[18:36:38] [Server thread/INFO]: Starting Minecraft server on *:25565
[18:36:38] [Server thread/WARN]: Failed to load user banlist:
java.io.FileNotFoundException: banned-players.json (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method) ~[?:?]
at java.base/java.io.FileInputStream.open(Unknown Source) ~[?:?]
at java.base/java.io.FileInputStream.<init>(Unknown Source) ~[?:?]
at com.google.common.io.Files.newReader(Files.java:86) ~[minecraft_server.1.7.10.jar:?]
at om.g(SourceFile:124) ~[minecraft_server.1.7.10.jar:?]
at ls.y(SourceFile:99) [minecraft_server.1.7.10.jar:?]
at ls.<init>(SourceFile:25) [minecraft_server.1.7.10.jar:?]
at lt.e(SourceFile:166) [minecraft_server.1.7.10.jar:?]
at net.minecraft.server.MinecraftServer.run(SourceFile:339) [minecraft_server.1.7.10.jar:?]
at lj.run(SourceFile:628) [minecraft_server.1.7.10.jar:?]
[18:36:38] [Server thread/WARN]: Failed to load ip banlist:
java.io.FileNotFoundException: banned-ips.json (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method) ~[?:?]
at java.base/java.io.FileInputStream.open(Unknown Source) ~[?:?]
at java.base/java.io.FileInputStream.<init>(Unknown Source) ~[?:?]
at com.google.common.io.Files.newReader(Files.java:86) ~[minecraft_server.1.7.10.jar:?]
at om.g(SourceFile:124) ~[minecraft_server.1.7.10.jar:?]
at ls.x(SourceFile:91) [minecraft_server.1.7.10.jar:?]
at ls.<init>(SourceFile:27) [minecraft_server.1.7.10.jar:?]
at lt.e(SourceFile:166) [minecraft_server.1.7.10.jar:?]
at net.minecraft.server.MinecraftServer.run(SourceFile:339) [minecraft_server.1.7.10.jar:?]
at lj.run(SourceFile:628) [minecraft_server.1.7.10.jar:?]
[18:36:38] [Server thread/WARN]: Failed to load operators list:
java.io.FileNotFoundException: ops.json (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method) ~[?:?]
at java.base/java.io.FileInputStream.open(Unknown Source) ~[?:?]
at java.base/java.io.FileInputStream.<init>(Unknown Source) ~[?:?]
at com.google.common.io.Files.newReader(Files.java:86) ~[minecraft_server.1.7.10.jar:?]
at om.g(SourceFile:124) ~[minecraft_server.1.7.10.jar:?]
at ls.z(SourceFile:107) [minecraft_server.1.7.10.jar:?]
at ls.<init>(SourceFile:29) [minecraft_server.1.7.10.jar:?]
at lt.e(SourceFile:166) [minecraft_server.1.7.10.jar:?]
at net.minecraft.server.MinecraftServer.run(SourceFile:339) [minecraft_server.1.7.10.jar:?]
at lj.run(SourceFile:628) [minecraft_server.1.7.10.jar:?]
[18:36:38] [Server thread/WARN]: Failed to load white-list:
java.io.FileNotFoundException: whitelist.json (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method) ~[?:?]
at java.base/java.io.FileInputStream.open(Unknown Source) ~[?:?]
at java.base/java.io.FileInputStream.<init>(Unknown Source) ~[?:?]
at com.google.common.io.Files.newReader(Files.java:86) ~[minecraft_server.1.7.10.jar:?]
at om.g(SourceFile:124) ~[minecraft_server.1.7.10.jar:?]
at ls.B(SourceFile:123) [minecraft_server.1.7.10.jar:?]
at ls.<init>(SourceFile:30) [minecraft_server.1.7.10.jar:?]
at lt.e(SourceFile:166) [minecraft_server.1.7.10.jar:?]
at net.minecraft.server.MinecraftServer.run(SourceFile:339) [minecraft_server.1.7.10.jar:?]
at lj.run(SourceFile:628) [minecraft_server.1.7.10.jar:?]
[18:36:38] [Server thread/INFO]: Preparing level "world"
[18:36:38] [Server thread/INFO]: Preparing start region for level 0
[18:36:39] [Server thread/INFO]: Preparing spawn area: 12%
[18:36:40] [Server thread/INFO]: Preparing spawn area: 28%
[18:36:41] [Server thread/INFO]: Preparing spawn area: 48%
[18:36:42] [Server thread/INFO]: Preparing spawn area: 69%
[18:36:43] [Server thread/INFO]: Preparing spawn area: 91%
[18:36:44] [Server thread/INFO]: Done (5.518s)! For help, type "help" or "?"
[18:36:44] [Server thread/INFO]: Starting remote control listener
[18:36:44] [RCON Listener #1/INFO]: RCON running on 0.0.0.0:25575

160
logs/java/java.go Normal file
View File

@ -0,0 +1,160 @@
package java
import (
"bufio"
"io"
"net/netip"
"slices"
"strconv"
"strings"
"time"
"sirherobrine23.com.br/go-bds/go-bds/logs"
"sirherobrine23.com.br/go-bds/go-bds/regex"
"sirherobrine23.com.br/go-bds/go-bds/utils/slice"
)
var (
_ = logs.RegisterParse[*JavaParse]("mojang/java") // Register platform
_ logs.Log = (*JavaParse)(nil)
_ logs.Player = (*JavaPlayer)(nil)
DoneMatch *regex.Regexp = regex.MustCompile(`Done \([0-9\.]+s\)! For help, type "help"( or "\?")?`)
)
type JavaPlayer struct {
Username string `json:"player"` // Player username
Actioned logs.Action `json:"action"` // Action type
Timed time.Time `json:"time"` // Action time
}
func (player JavaPlayer) Name() string { return player.Username }
func (player JavaPlayer) Action() logs.Action { return player.Actioned }
func (player JavaPlayer) Time() time.Time { return player.Timed }
func (player JavaPlayer) XUID() int64 { return -1 }
type JavaParse struct {
ServerPlaform *logs.Server `json:"info"`
Players map[string][]logs.Player `json:"players"`
Errs []error `json:"errors"`
Warngs []error `json:"warnings"`
}
func (java JavaParse) Server() *logs.Server { return java.ServerPlaform }
func (java JavaParse) Errors() []error { return java.Errs }
func (java JavaParse) Warnings() []error { return java.Warngs }
func (java JavaParse) GetPlayer(name string) (player []logs.Player, ok bool) {
player, ok = java.Players[name]
return
}
func (java *JavaParse) Parse(log io.Reader) error { return java.ParseTime(time.Now(), log) }
func (java *JavaParse) ParseTime(currentTime time.Time, log io.Reader) error {
java.ServerPlaform = &logs.Server{Platform: "mojang/java", Ports: []*logs.Port{}} // Init info
java.Errs, java.Warngs = []error{}, []error{}
java.Players = map[string][]logs.Player{}
valid, errorReference, scanner := false, error(nil), bufio.NewScanner(log)
for scanner.Scan() {
line := scanner.Text()
valid = true
if !(line[0] == 'U' || line[0] == 'S' || line[0] == '[') && errorReference != nil {
errorReference.(*logs.ErrorReference).Line = append(errorReference.(*logs.ErrorReference).Line, line)
continue
} else if !(line[0] == 'U' || line[0] == 'S' || line[0] == '[') {
valid = false
break
}
if line[0] == 'U' || line[0] == 'S' {
continue // Ignore line
}
prefixSplited := [3]string(strings.SplitAfterN(line, "]", 3))
prefixSplited[0] = strings.Replace(strings.Replace(strings.TrimSpace(prefixSplited[0][1:]), "[", "", 1), "]", "", 1)
prefixSplited[1] = strings.Replace(strings.Replace(strings.TrimSpace(prefixSplited[1][1:]), "[", "", 1), "]", "", 1)
prefixSplited[2] = strings.TrimSpace(prefixSplited[2][1:])
// Error log
if strings.HasSuffix(prefixSplited[1], "WARN") || strings.HasSuffix(prefixSplited[1], "ERROR") {
errorReference = &logs.ErrorReference{
LogLevel: 1,
FistLine: prefixSplited[2],
}
java.Warngs = append(java.Warngs, errorReference)
continue
} else if prefixSplited[0][0:4] == "Log4" { // log4j ignore
continue
}
errorReference = nil
timeMoment, err := time.ParseInLocation(time.TimeOnly, prefixSplited[0], time.Local)
if err != nil {
return err
}
currentTime = currentTime.Add(time.Hour*time.Duration(timeMoment.Hour()) + time.Minute*time.Duration(timeMoment.Minute()) + time.Second*time.Duration(timeMoment.Second()))
if DoneMatch.Match([]byte(prefixSplited[2])) {
java.ServerPlaform.Started = currentTime
continue
}
contentExplode := slice.Slice[string](strings.Fields(prefixSplited[2]))
switch contentExplode.At(0) {
case "RCON":
if contentExplode.At(-2) == "on" {
addr, err := netip.ParseAddrPort(contentExplode.At(-1))
if err != nil {
return err
}
java.ServerPlaform.Ports = append(java.ServerPlaform.Ports, &logs.Port{AddrPort: addr, From: "RCON"})
}
case "Starting":
switch contentExplode.At(-2) {
case "version":
version := contentExplode.At(-1)
java.ServerPlaform.Version = version
case "on":
Value := contentExplode.At(-1)
if Value[0] == '*' {
Value = Value[2:]
}
port, _ := strconv.ParseInt(Value, 10, 16)
java.ServerPlaform.Ports = append(java.ServerPlaform.Ports, &logs.Port{AddrPort: netip.AddrPortFrom(netip.IPv4Unspecified(), uint16(port)), From: "TCP"})
}
default:
switch contentExplode.At(-1) {
case "game":
at3 := strings.ToLower(contentExplode.At(-3))
if slices.Contains([]string{"left", "joined"}, at3) {
playerName := prefixSplited[2][:strings.LastIndex(prefixSplited[2], contentExplode.At(-3))-1]
if _, ok := java.Players[playerName]; !ok {
java.Players[playerName] = []logs.Player{}
}
action := logs.Action(0)
switch at3 {
case "joined":
action = logs.Connect
case "left":
action = logs.Disconnect
}
// Append to struct
java.Players[playerName] = append(java.Players[playerName], JavaPlayer{
Username: playerName,
Actioned: action,
Timed: currentTime,
})
}
}
}
}
if err := scanner.Err(); err != nil {
return err
} else if valid { // return nil if platform is valid
return nil
}
return logs.ErrSkipPlatform
}

35
logs/java/java_test.go Normal file
View File

@ -0,0 +1,35 @@
package java
import (
_ "embed"
"encoding/json"
"strings"
"testing"
)
func testPrintLog(t *testing.T, textPrint string, log *JavaParse) {
d, _ := json.MarshalIndent(log, "", " ")
t.Logf(textPrint, string(d))
}
func TestDetectAndParseJava(t *testing.T) {
parsedLog, err := &JavaParse{}, error(nil)
if err = parsedLog.Parse(strings.NewReader(StaticLogFileJava1)); err != nil {
t.Errorf("Cannot parse java log 1: %s", err)
return
}
testPrintLog(t, "Parsed log java Static 1:\n%s", parsedLog)
if err = parsedLog.Parse(strings.NewReader(StaticLogFileJava2)); err != nil {
t.Errorf("Cannot parse java log 2: %s", err)
return
}
testPrintLog(t, "Parsed log java Static 2:\n%s", parsedLog)
}
var (
//go:embed 1.21.4.txt
StaticLogFileJava1 string
//go:embed 1.7.10.txt
StaticLogFileJava2 string
)

162
logs/logs.go Normal file
View File

@ -0,0 +1,162 @@
package logs
import (
"bytes"
"errors"
"fmt"
"io"
"net/netip"
"reflect"
"strings"
"time"
)
var (
ErrPlayerNotExist error = errors.New("player not exists") // Player not exists or not never connected in server session
ErrSkipPlatform error = errors.New("skip platform parse") // Skip current platform parse and continue to next if avaible
ErrCannotDetectPlatform error = errors.New("cannot detect log platform") // Platform log not detected or not loaded
reflectParse = map[string]reflect.Type{}
)
// Register parse to log Dectect platform
func RegisterParse[Parse Log](name string) bool {
if _, ok := reflectParse[name]; !ok {
reflectParse[name] = reflect.TypeFor[Parse]()
}
return true
}
type Action int // Player actions
const (
_ Action = iota
Disconnect // Player disconnected from server
Connect // Player connected to server
Spawned // Player connected to server and spawned in world server
Banned // Player is banned from server by operator
PlayerActionsValid = Disconnect | Connect | Spawned | Banned // Check if actions is valid
)
func (act Action) String() string {
switch act {
case Disconnect:
return "disconnect"
case Connect:
return "connect"
case Spawned:
return "spawned"
case Banned:
return "banned"
default:
return "unknown"
}
}
func (act Action) MarshalText() ([]byte, error) {
return []byte(act.String()), nil
}
func (act *Action) UnmarshalText(data []byte) error {
switch string(data) {
case "disconnect":
*act = Disconnect
case "connect":
*act = Connect
case "spawned":
*act = Spawned
case "banned":
*act = Banned
default:
return fmt.Errorf("unknown action: %s", data)
}
return nil
}
type Port struct {
AddrPort netip.AddrPort `json:"addr"` // Port and ip address
From string `json:"from"` // Server listened protocol, TCP/UDP/Unix...
}
var _ error = &ErrorReference{}
type ErrorReference struct {
LogLevel int
FistLine string
Line []string
}
func (err *ErrorReference) Error() string {
errLevel := "error"
if err.LogLevel == 1 {
errLevel = "warning"
} else if err.LogLevel >= 2 {
errLevel = "fatal"
}
return strings.TrimSpace(fmt.Sprintf("%s: %s", errLevel, err.FistLine))
}
func (err *ErrorReference) MarshalText() ([]byte, error) {
return []byte(err.Error()), nil
}
func (err *ErrorReference) Unwrap() []error {
return []error{
err,
errors.New(strings.Join(err.Line, "\n")),
}
}
// Server info
type Server struct {
Version string `json:"version"` // Server version if avaible
Started time.Time `json:"started"` // Time server started or avaible to connect
Platform string `json:"platform"` // Server platform, example: bedrock, java, pocketmine...
Ports []*Port `json:"ports"` // Server ports listened
}
type Player interface {
Name() string // Player username
Action() Action // Player action
Time() time.Time // Action time
XUID() int64 // Xbox XUID
}
// Implements log parse and return based log info
type Log interface {
ParseTime(time.Time, io.Reader) error // Parse log with server date
Parse(io.Reader) error // Parse log with current time
Server() *Server // Server info
GetPlayer(string) ([]Player, bool) // Get player info
Errors() []error // Get log errors
Warnings() []error // Get log warnings
}
// Parse log
func Parse(log io.ReadSeeker) (Log, error) {
for _, typeOf := range reflectParse {
platformLog := reflect.New(typeOf.Elem()).Interface().(Log)
if err := platformLog.Parse(log); err != nil {
if err == ErrSkipPlatform {
if _, err = log.Seek(0, io.SeekStart); err == nil {
continue // Skip
}
err = fmt.Errorf("cannot seek log file: %s", err)
}
return nil, err
}
return platformLog, nil
}
return nil, ErrCannotDetectPlatform
}
// Parse string log
func ParseString(log string) (Log, error) {
return Parse(strings.NewReader(log))
}
// Parse buffer log
func ParseBuffer(log []byte) (Log, error) {
return Parse(bytes.NewReader(log))
}

35
logs/logs_test.go Normal file
View File

@ -0,0 +1,35 @@
package logs_test
import (
"embed"
"fmt"
"io"
"io/fs"
"testing"
"sirherobrine23.com.br/go-bds/go-bds/logs"
_ "sirherobrine23.com.br/go-bds/go-bds/logs/bedrock"
_ "sirherobrine23.com.br/go-bds/go-bds/logs/java"
)
//go:embed */1.*.txt
var LogFiles embed.FS
func TestLogs(t *testing.T) {
err := fs.WalkDir(LogFiles, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
} else if d.IsDir() {
return nil
}
file, _ := LogFiles.Open(path)
if _, err = logs.Parse(file.(io.ReadSeeker)); err != nil {
return fmt.Errorf("cannot parse %s: %s", path, err)
}
return nil
})
if err != nil {
t.Error(err)
}
}

100
logs/mclog/fs.go Normal file
View File

@ -0,0 +1,100 @@
package mclog
import (
"crypto/rand"
"fmt"
"io"
"io/fs"
"os"
"path"
"path/filepath"
"strings"
"sirherobrine23.com.br/go-bds/go-bds/overlayfs"
)
var (
_ FileSystem = Local("")
_ FileSystem = &Mergefs{}
)
type File interface {
fs.File
io.Seeker
io.Writer
}
type FileSystem interface {
fs.FS
Create(string) (File, error)
Mkdir(string, fs.FileMode) error
}
func MkdirAll(fss FileSystem, dir string, perm fs.FileMode) error {
paths := strings.Split(filepath.ToSlash(dir), "/")
for pathIndex := range paths {
if err := fss.Mkdir(path.Join(paths[:pathIndex]...), perm); err != nil {
return err
}
}
return nil
}
func CreateID(fss FileSystem, dir string) (string, File, error) {
dir = path.Clean(filepath.ToSlash(dir))
if _, err := fs.Stat(fss, dir); err != nil {
if err := MkdirAll(fss, dir, 0755); err != nil {
return "", nil, err
}
}
id := make([]byte, 16)
for attemps := 8; attemps > 0; attemps-- {
if _, err := rand.Read(id); err != nil {
return "", nil, &fs.PathError{Op: "create", Path: dir, Err: err}
}
id[6] = (id[6] & 0x0f) | 0x40
id[8] = (id[8] & 0x3f) | 0x80
name := fmt.Sprintf("%x", id)
if _, err := fs.Stat(fss, path.Join(dir, name)); err == nil {
continue // Skip exists
} else if file, err := fss.Create(path.Join(dir, name)); err == nil {
return name, file, nil
}
}
return "", nil, &fs.PathError{Op: "create", Path: dir, Err: fmt.Errorf("cannot make file id")}
}
// Maneger log files in Local disk
type Local string
func (local Local) Open(name string) (fs.File, error) { return os.OpenInRoot(string(local), name) }
func (local Local) Mkdir(name string, perm fs.FileMode) error {
r, err := os.OpenRoot(string(local))
if err != nil {
return err
}
defer r.Close()
return r.Mkdir(name, perm.Perm())
}
func (local Local) Create(name string) (File, error) {
r, err := os.OpenRoot(string(local))
if err != nil {
return nil, err
}
defer r.Close()
return r.Create(name)
}
type Mergefs overlayfs.FsMergeFs
func (fss Mergefs) Open(name string) (fs.File, error) { return overlayfs.FsMergeFs(fss).Open(name) }
func (fss Mergefs) Mkdir(name string, perm fs.FileMode) error {
return overlayfs.FsMergeFs(fss).MergedFS.Mkdir(filepath.Join(fss.Subdir, filepath.Clean(name)), perm.Perm())
}
func (fss Mergefs) Create(name string) (File, error) {
if fss.MergedFS == nil {
return nil, fs.ErrInvalid
}
return fss.MergedFS.Create(filepath.Join(fss.Subdir, filepath.Clean(name)))
}

View File

@ -2,7 +2,10 @@ package mclog
import ( import (
"errors" "errors"
"fmt"
"time" "time"
"sirherobrine23.com.br/go-bds/go-bds/logs"
) )
var ( var (
@ -23,6 +26,12 @@ const (
LogWarn LogLevel = "warning" LogWarn LogLevel = "warning"
) )
type Limits struct {
StorageTime time.Duration `json:"storageTime"` // The duration in seconds that a log is stored for after the last view.
MaxLength int64 `json:"maxLength"` // Maximum file length in bytes. Logs over this limit will be truncated to this length.
MaxLines int64 `json:"maxLines"` // Maximum number of lines. Additional lines will be removed.
}
// Stands log levels // Stands log levels
type LogLevel string type LogLevel string
@ -32,12 +41,6 @@ type MclogResponseStatus struct {
Id string `json:"id,omitempty"` // If post log file return id if processed Id string `json:"id,omitempty"` // If post log file return id if processed
} }
type Limits struct {
StorageTime time.Duration `json:"storageTime"` // The duration in seconds that a log is stored for after the last view.
MaxLength int64 `json:"maxLength"` // Maximum file length in bytes. Logs over this limit will be truncated to this length.
MaxLines int64 `json:"maxLines"` // Maximum number of lines. Additional lines will be removed.
}
type EntryLine struct { type EntryLine struct {
Numbers int `json:"number"` Numbers int `json:"number"`
Content string `json:"content"` Content string `json:"content"`
@ -68,3 +71,41 @@ type Insights struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Analysis map[LogLevel][]*InsightsAnalysis `json:"analysis"` Analysis map[LogLevel][]*InsightsAnalysis `json:"analysis"`
} }
func ConvertLogs(id string, log logs.Log) Insights {
var insight Insights
insight.Type = "server"
insight.ID = id
serverInfo := log.Server()
insight.Version = serverInfo.Version
insight.Name = serverInfo.Platform
insight.Title = fmt.Sprintf("%s - %s", serverInfo.Platform, serverInfo.Version)
insight.Analysis = map[LogLevel][]*InsightsAnalysis{}
insight.Analysis[LogInfo] = append(insight.Analysis[LogInfo], &InsightsAnalysis{
Label: "Started time",
Value: serverInfo.Started.Format(time.RFC3339),
})
for _, port := range serverInfo.Ports {
insight.Analysis[LogInfo] = append(insight.Analysis[LogInfo], &InsightsAnalysis{
Label: "Port listen",
Message: port.From,
Value: port.AddrPort.String(),
})
}
for _, err := range log.Errors() {
insight.Analysis[LogProblem] = append(insight.Analysis[LogProblem], &InsightsAnalysis{
Value: err.Error(),
})
}
for _, err := range log.Warnings() {
insight.Analysis[LogWarn] = append(insight.Analysis[LogWarn], &InsightsAnalysis{
Value: err.Error(),
})
}
return insight
}

365
logs/mclog/server.go Normal file
View File

@ -0,0 +1,365 @@
package mclog
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"maps"
"net/http"
"path"
"runtime"
"slices"
"strings"
"sirherobrine23.com.br/go-bds/go-bds/logs"
)
func writeJson(w http.ResponseWriter, code int, v any) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(code)
jsenc := json.NewEncoder(w)
jsenc.SetIndent("", " ")
jsenc.Encode(v)
}
func NewHandler(limits Limits, fss FileSystem) http.Handler {
fss.Mkdir("v2", 0755) // Ignore error's
mux, v1, v2 := http.NewServeMux(), http.NewServeMux(), http.NewServeMux()
// mclogs limits
v1Limits, _ := json.MarshalIndent(map[string]any{
"storageTime": int(limits.StorageTime.Seconds()),
"maxLength": limits.MaxLength,
"maxLines": limits.MaxLines,
}, "", " ")
// V1 from mclogs
v1.HandleFunc("GET /limits", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
w.Write(v1Limits)
})
// Storage log
v1.HandleFunc("POST /log", func(w http.ResponseWriter, r *http.Request) {
if slices.Contains(r.Header.Values("Content-Type"), "application/x-www-form-urlencoded") {
r.ParseForm()
}
if !(r.Form.Has("content") || r.Form.Get("content") == "") {
writeJson(w, 400, map[string]any{
"success": false,
"error": "Required POST argument 'content' is empty.",
})
return
}
if limits.MaxLines > 0 || limits.MaxLength > 0 {
lines := strings.Split(r.Form.Get("content"), "\n")
content := strings.Join(lines[:min(len(lines)-1, int(limits.MaxLines))], "\n")
if limits.MaxLength > 0 {
content = content[:min(len(content), int(limits.MaxLength))]
}
r.Form.Set("content", content)
}
if _, err := logs.ParseString(r.Form.Get("content")); err != nil {
writeJson(w, 400, map[string]any{
"success": false,
"error": err.Error(),
})
return
}
id, file, err := CreateID(fss, ".")
if err != nil {
writeJson(w, 500, map[string]any{
"success": false,
"error": fmt.Errorf("canont create file and id: %s", err).Error(),
})
return
}
defer file.Close()
if _, err = io.WriteString(file, r.Form.Get("content")); err != nil {
writeJson(w, 500, map[string]any{
"success": false,
"error": fmt.Errorf("canont write log to file: %s", err).Error(),
})
return
}
writeJson(w, 200, map[string]any{
"success": true,
"id": id,
})
})
// Get log
v1.HandleFunc("GET /raw/{id}", func(w http.ResponseWriter, r *http.Request) {
file, err := fss.Open(r.PathValue("id"))
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
writeJson(w, 404, map[string]any{
"success": false,
"error": "Log not found.",
})
} else {
writeJson(w, 400, map[string]any{
"success": false,
"error": err.Error(),
})
}
return
}
defer file.Close()
w.Header().Set("Content-Type", "text/plain")
w.WriteHeader(200)
io.Copy(w, file)
})
// Get log insights
v1.HandleFunc("GET /insights/{id}", func(w http.ResponseWriter, r *http.Request) {
file, err := fss.Open(r.PathValue("id"))
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
writeJson(w, 404, map[string]any{
"success": false,
"error": "Log not found.",
})
} else {
writeJson(w, 400, map[string]any{
"success": false,
"error": err.Error(),
})
}
return
}
defer file.Close()
buff, err := io.ReadAll(file)
if err != nil {
writeJson(w, 500, map[string]any{
"success": false,
"error": err.Error(),
})
return
}
log, err := logs.ParseBuffer(buff)
if err != nil {
writeJson(w, 500, map[string]any{
"success": false,
"error": err.Error(),
})
return
}
writeJson(w, 500, ConvertLogs(r.PathValue("id"), log))
})
// Analyse a log without saving it
v1.HandleFunc("POST /analyse", func(w http.ResponseWriter, r *http.Request) {
if slices.Contains(r.Header.Values("Content-Type"), "application/x-www-form-urlencoded") {
r.ParseForm()
}
if !(r.Form.Has("content") || r.Form.Get("content") == "") {
writeJson(w, 400, map[string]any{
"success": false,
"error": "Required POST argument 'content' is empty.",
})
return
}
if limits.MaxLines > 0 || limits.MaxLength > 0 {
lines := strings.Split(r.Form.Get("content"), "\n")
content := strings.Join(lines[:min(len(lines)-1, int(limits.MaxLines))], "\n")
if limits.MaxLength > 0 {
content = content[:min(len(content), int(limits.MaxLength))]
}
r.Form.Set("content", content)
}
log, err := logs.ParseString(r.Form.Get("content"))
if err != nil {
writeJson(w, 400, map[string]any{
"success": false,
"error": err.Error(),
})
return
}
writeJson(w, 200, ConvertLogs("", log))
})
// Server v2
v2Info, _ := json.MarshalIndent(map[string]any{"runtime": runtime.Version(), "limits": limits}, "", " ")
v2.HandleFunc("GET /{$}", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
w.Write(v2Info)
w.Write(nil)
})
v2.HandleFunc("POST /{$}", func(w http.ResponseWriter, r *http.Request) {
logString, parsedLogs := []string{}, map[string]logs.Log{}
if slices.Contains(r.Header.Values("Content-Type"), "application/octet-stream") {
body, err := io.ReadAll(r.Body)
if err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("cannot read json body: %s", err).Error()})
return
}
logString = append(logString, string(body))
} else if slices.Contains(r.Header.Values("Content-Type"), "multipart/form-data") {
if err := r.ParseMultipartForm(10 << 20); err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("cannot parse form: %s", err).Error()})
return
}
for _, content := range r.MultipartForm.Value {
logString = append(logString, content...)
}
for _, content := range r.MultipartForm.File {
for _, file := range content {
info, err := file.Open()
if err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("cannot read json body: %s", err).Error()})
return
}
defer info.Close()
body, err := io.ReadAll(info)
if err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("cannot read json body: %s", err).Error()})
return
}
logString = append(logString, string(body))
}
}
} else if slices.Contains(r.Header.Values("Content-Type"), "application/x-www-form-urlencoded") {
if err := r.ParseForm(); err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("cannot parse form: %s", err).Error()})
return
}
for _, content := range r.Form {
logString = append(logString, content...)
}
} else if slices.Contains(r.Header.Values("Content-Type"), "application/json") {
body, err := io.ReadAll(r.Body)
if err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("cannot read json body: %s", err).Error()})
return
}
var data any
if err = json.Unmarshal(body, &data); err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("cannot parse json: %s", err).Error()})
return
}
switch v := data.(type) {
case []string:
logString = v
case map[string]string:
logString = slices.Collect(maps.Values(v))
default:
writeJson(w, 400, map[string]any{"error": "invalid data type", "tips": "[]string, map[string]string"})
return
}
} else {
writeJson(w, 400, map[string]any{"error": "require Content-Type in header"})
return
}
err := error(nil)
for _, log := range logString {
log = strings.TrimSpace(log)
sum := sha256.Sum256([]byte(log))
if parsedLogs[hex.EncodeToString(sum[:])], err = logs.ParseString(log); err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("cannot parse json: %s", err).Error()})
return
}
}
writeJson(w, 200, parsedLogs)
})
v2.HandleFunc("POST /raw", func(w http.ResponseWriter, r *http.Request) {
if !slices.Contains(r.Header.Values("Content-Type"), "application/octet-stream") {
writeJson(w, 400, map[string]any{"error": "require application/octet-stream on Content-Type header"})
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("cannot get full body: %s", err).Error()})
return
}
body = []byte(strings.TrimSpace(string(body)))
if _, err = logs.ParseBuffer(body); err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("invalid log: %s", err).Error()})
return
}
fileSum := sha256.Sum256(body)
fileID := hex.EncodeToString(fileSum[:])
remoteFile, err := fss.Open(path.Join("v2", fileID))
if remoteFile != nil {
remoteFile.Close() // Close file
}
if err != nil {
if !errors.Is(err, fs.ErrNotExist) {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("fs error: %s", err).Error()})
return
}
create, err := fss.Create(path.Join("v2", fileID))
if err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("fs error: %s", err).Error()})
return
}
create.Write(body)
create.Close()
}
writeJson(w, 200, map[string]any{"id": fileID})
})
v2.HandleFunc("GET /raw/{id}", func(w http.ResponseWriter, r *http.Request) {
file, err := fss.Open(path.Join("v2", r.PathValue("id")))
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
writeJson(w, 404, map[string]any{"error": "file not exists"})
} else {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("fs error: %s", err).Error()})
}
return
}
defer file.Close()
io.Copy(w, file)
})
v2.HandleFunc("GET /insight/{id}", func(w http.ResponseWriter, r *http.Request) {
file, err := fss.Open(path.Join("v2", r.PathValue("id")))
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
writeJson(w, 404, map[string]any{"error": "file not exists"})
} else {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("fs error: %s", err).Error()})
}
return
}
defer file.Close()
body, err := io.ReadAll(file)
if err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("read: %s", err).Error()})
return
}
log, err := logs.ParseBuffer(body)
if err != nil {
writeJson(w, 400, map[string]any{"error": fmt.Errorf("logs parse: %s", err).Error()})
return
}
writeJson(w, 200, log)
})
mux.Handle("/1/", http.StripPrefix("/1", v1))
mux.Handle("/v1/", http.StripPrefix("/v1", v1))
mux.Handle("/v2/", http.StripPrefix("/v2", v2))
return mux
}

38
logs/mclog/server/main.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"flag"
"fmt"
"net"
"net/http"
"net/netip"
"os"
"path/filepath"
_ "sirherobrine23.com.br/go-bds/go-bds/logs/bedrock"
_ "sirherobrine23.com.br/go-bds/go-bds/logs/java"
"sirherobrine23.com.br/go-bds/go-bds/logs/mclog"
)
var (
PortPoint = flag.Int("port", 0, "Port to listen http server")
Rootdir = flag.String("root", filepath.Join(os.TempDir(), "bdsmclogs"), "Folder to save log files")
)
func main() {
flag.Parse()
rootDir, port := *Rootdir, uint16(*PortPoint)
os.MkdirAll(rootDir, 0755)
limits := mclog.Limits{}
handler := mclog.NewHandler(limits, mclog.Local(rootDir))
listen, err := net.ListenTCP("tcp", net.TCPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), port)))
if err != nil {
fmt.Fprintf(os.Stderr, "cannot listen http server: %s\n", err)
os.Exit(1)
return
}
fmt.Fprintf(os.Stderr, "HTTP server listening on %s\n", listen.Addr().String())
http.Serve(listen, handler)
}

View File

@ -1,80 +0,0 @@
package mclog
import (
"bytes"
"errors"
"io"
)
var (
ErrCannotParse error = errors.New("cannot detect server")
ErrSkipParse error = errors.New("skip parse log")
)
type ServerParse interface {
Detect(log io.ReadSeeker) error // Check if log is compatible and parse
Insight() *Insights // Return mclog Insights
}
type PlatformParse interface {
String() string // Platform name, ex: 'mojang:bedrock', 'mojang:java', 'spigot', 'paper', 'pocketmine'
New() (ServerParse, error) // Return new server parse
}
var logParses = []PlatformParse{}
func ParsesRegitred() []string {
names := []string{}
for _, n := range logParses {
names = append(names, n.String())
}
return names
}
// Add new server log process
func RegisterNewParse(loger PlatformParse) bool {
if loger == nil {
return false
}
for _, value := range logParses {
if value == loger || value.String() == loger.String() {
return false
}
}
logParses = append(logParses, loger)
return true
}
// Parse log
func ParseLog(input io.Reader) (*Insights, error) {
if st, ok := input.(io.ReadSeeker); !ok {
buffer, err := io.ReadAll(st)
if err != nil {
return nil, err
}
input = bytes.NewReader(buffer)
}
reader := input.(io.ReadSeeker)
for _, server := range logParses {
parse, err := server.New()
if err != nil {
return nil, err
}
err = parse.Detect(reader)
switch err {
case ErrSkipParse:
// Reset stream
if _, err = reader.Seek(0, io.SeekStart); err != nil {
return nil, err
}
continue // Skip parse
case nil:
return parse.Insight(), nil
default:
return nil, err
}
}
return nil, ErrCannotParse
}

View File

@ -1,344 +0,0 @@
package mclog_test
import (
"encoding/json"
"strings"
"testing"
. "sirherobrine23.com.br/go-bds/go-bds/mclog"
_ "sirherobrine23.com.br/go-bds/go-bds/bedrock"
_ "sirherobrine23.com.br/go-bds/go-bds/java"
)
func TestLog(t *testing.T) {
var Insights []*Insights
for _, logString := range Loger {
insight, err := ParseLog(strings.NewReader(logString))
if err != nil {
t.Errorf("%s:\n%s", err, logString)
return
}
Insights = append(Insights, insight)
}
d, _ := json.MarshalIndent(Insights, "", " ")
println(string(d))
}
var (
Loger = []string{
`NO LOG FILE! - setting up server logging...
[2022-11-16 19:34:11:606 INFO] Starting Server
[2022-11-16 19:34:11:606 INFO] Version 1.19.41.01
[2022-11-16 19:34:11:606 INFO] Session ID d914c8d5-2ad1-46fd-b213-e76ed4e6caca
[2022-11-16 19:34:11:606 INFO] Level Name: Sirherobrine23
[2022-11-16 19:34:11:609 INFO] Game mode: 0 Survival
[2022-11-16 19:34:11:609 INFO] Difficulty: 2 NORMAL
[2022-11-16 19:34:11:729 INFO] opening worlds/Sirherobrine23/db
[2022-11-16 19:34:12:336 INFO] IPv4 supported, port: 19132
[2022-11-16 19:34:12:337 INFO] IPv6 supported, port: 19133
[2022-11-16 19:34:13:002 INFO] Server started.
[2022-11-16 19:34:13:008 INFO] Please note that LAN discovery will not function for this server.
[2022-11-16 19:34:13:008 INFO] Server IP must be specified in Servers tab in game.
[2022-11-16 19:34:13:052 INFO] ================ TELEMETRY MESSAGE ===================
[2022-11-16 19:34:13:052 INFO] Server Telemetry is currently not enabled.
[2022-11-16 19:34:13:052 INFO] Enabling this telemetry helps us improve the game.
[2022-11-16 19:34:13:052 INFO]
[2022-11-16 19:34:13:052 INFO] To enable this feature, add the line 'emit-server-telemetry=true'
[2022-11-16 19:34:13:052 INFO] to the server.properties file in the handheld/src-server directory
[2022-11-16 19:34:13:052 INFO] ======================================================
[2022-11-16 19:55:44:503 INFO] Player connected: Sirherobrine, xuid: 2535413418839840
[2022-11-16 19:55:49:400 INFO] Player Spawned: Sirherobrine xuid: 2535413418839840
[2022-11-16 19:56:59:378 INFO] Player disconnected: Sirherobrine, xuid: 2535413418839840
[2022-11-16 20:01:48:937 INFO] Running AutoCompaction...
[2022-11-16 20:07:48:959 INFO] Running AutoCompaction...
[2022-11-19 19:23:22:600 INFO] Running AutoCompaction...
[2022-11-19 19:29:22:601 INFO] Running AutoCompaction...
[2022-11-19 19:35:22:602 INFO] Running AutoCompaction...
[2022-11-19 19:41:23:746 INFO] Running AutoCompaction...
[2022-11-19 19:47:23:746 INFO] Running AutoCompaction...
[2022-11-19 19:53:23:747 INFO] Running AutoCompaction...
[2022-11-19 19:59:23:748 INFO] Running AutoCompaction...
[2022-11-19 20:05:24:904 INFO] Running AutoCompaction...
[2022-11-19 20:07:15:776 INFO] Player connected: Sirherobrine, xuid: 2535413418839840
[2022-11-19 20:07:30:911 INFO] Player Spawned: Sirherobrine xuid: 2535413418839840`,
`NO LOG FILE! - setting up server logging...
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Starting Server
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Version 1.6.1.0
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Level Name: Bedrock level
NO LOG FILE! - [2025-01-19 23:35:13 ERROR] Error opening whitelist file: whitelist.json
NO LOG FILE! - [2025-01-19 23:35:13 ERROR] Error opening ops file: ops.json
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Game mode: 0 Survival
NO LOG FILE! - [2025-01-19 23:35:13 INFO] Difficulty: 1 EASY
NO LOG FILE! - [2025-01-19 23:35:13 INFO] IPv4 supported, port: 19132
NO LOG FILE! - [2025-01-19 23:35:13 INFO] IPv6 supported, port: 19133
NO LOG FILE! - [2025-01-19 23:35:14 INFO] Listening on IPv6 port: 19133
NO LOG FILE! - [2025-01-19 23:35:14 INFO] Listening on IPv4 port: 19132
NO LOG FILE! - [2025-01-19 23:35:18 INFO] Player connected: 2535413418839840
NO LOG FILE! - [2025-01-19 23:38:54 INFO] Player disconnected: 2535413418839840
NO LOG FILE! - [2025-01-19 23:40:13 INFO] Player connected:
NO LOG FILE! - [2025-01-19 23:40:54 INFO] Player disconnected:
`,
`Unpacking 1.21.4/server-1.21.4.jar (versions:1.21.4) to versions/1.21.4/server-1.21.4.jar
Unpacking com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar (libraries:com.fasterxml.jackson.core:jackson-annotations:2.13.4) to libraries/com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar
Unpacking com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar (libraries:com.fasterxml.jackson.core:jackson-core:2.13.4) to libraries/com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar
Unpacking com/fasterxml/jackson/core/jackson-databind/**.**.**.**/jackson-databind-2.13.4.2.jar (libraries:com.fasterxml.jackson.core:jackson-databind:**.**.**.**) to libraries/com/fasterxml/jackson/core/jackson-databind/**.**.**.**/jackson-databind-2.13.4.2.jar
Unpacking com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar (libraries:com.github.oshi:oshi-core:6.6.5) to libraries/com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar
Unpacking com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar (libraries:com.github.stephenc.jcip:jcip-annotations:1.0-1) to libraries/com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar
Unpacking com/google/code/gson/gson/2.11.0/gson-2.11.0.jar (libraries:com.google.code.gson:gson:2.11.0) to libraries/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar
Unpacking com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar (libraries:com.google.guava:failureaccess:1.0.2) to libraries/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar
Unpacking com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar (libraries:com.google.guava:guava:33.3.1-jre) to libraries/com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar
Unpacking com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar (libraries:com.microsoft.azure:msal4j:1.17.2) to libraries/com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar
Unpacking com/mojang/authlib/6.0.57/authlib-6.0.57.jar (libraries:com.mojang:authlib:6.0.57) to libraries/com/mojang/authlib/6.0.57/authlib-6.0.57.jar
Unpacking com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar (libraries:com.mojang:brigadier:1.3.10) to libraries/com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar
Unpacking com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar (libraries:com.mojang:datafixerupper:8.0.16) to libraries/com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar
Unpacking com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar (libraries:com.mojang:jtracy:1.0.29) to libraries/com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar
Unpacking com/mojang/logging/1.5.10/logging-1.5.10.jar (libraries:com.mojang:logging:1.5.10) to libraries/com/mojang/logging/1.5.10/logging-1.5.10.jar
Unpacking com/nimbusds/content-type/2.3/content-type-2.3.jar (libraries:com.nimbusds:content-type:2.3) to libraries/com/nimbusds/content-type/2.3/content-type-2.3.jar
Unpacking com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar (libraries:com.nimbusds:lang-tag:1.7) to libraries/com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar
Unpacking com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar (libraries:com.nimbusds:nimbus-jose-jwt:9.40) to libraries/com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar
Unpacking com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar (libraries:com.nimbusds:oauth2-oidc-sdk:11.18) to libraries/com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar
Unpacking commons-io/commons-io/2.17.0/commons-io-2.17.0.jar (libraries:commons-io:commons-io:2.17.0) to libraries/commons-io/commons-io/2.17.0/commons-io-2.17.0.jar
Unpacking io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar (libraries:io.netty:netty-buffer:4.1.115.Final) to libraries/io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar
Unpacking io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar (libraries:io.netty:netty-codec:4.1.115.Final) to libraries/io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar
Unpacking io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar (libraries:io.netty:netty-common:4.1.115.Final) to libraries/io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar
Unpacking io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar (libraries:io.netty:netty-handler:4.1.115.Final) to libraries/io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar
Unpacking io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar (libraries:io.netty:netty-resolver:4.1.115.Final) to libraries/io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar
Unpacking io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar (libraries:io.netty:netty-transport:4.1.115.Final) to libraries/io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar
Unpacking io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar (libraries:io.netty:netty-transport-classes-epoll:4.1.115.Final) to libraries/io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar
Unpacking io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar (libraries:io.netty:netty-transport-native-epoll:4.1.115.Final:linux-x86_64) to libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar
Unpacking io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar (libraries:io.netty:netty-transport-native-epoll:4.1.115.Final:linux-aarch_64) to libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar
Unpacking io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar (libraries:io.netty:netty-transport-native-unix-common:4.1.115.Final) to libraries/io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar
Unpacking it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar (libraries:it.unimi.dsi:fastutil:8.5.15) to libraries/it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar
Unpacking net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar (libraries:net.java.dev.jna:jna:5.15.0) to libraries/net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar
Unpacking net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar (libraries:net.java.dev.jna:jna-platform:5.15.0) to libraries/net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar
Unpacking net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar (libraries:net.minidev:accessors-smart:2.5.1) to libraries/net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar
Unpacking net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar (libraries:net.minidev:json-smart:2.5.1) to libraries/net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar
Unpacking net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar (libraries:net.sf.jopt-simple:jopt-simple:5.0.4) to libraries/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar
Unpacking org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar (libraries:org.apache.commons:commons-lang3:3.17.0) to libraries/org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar
Unpacking org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar (libraries:org.apache.logging.log4j:log4j-api:2.24.1) to libraries/org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar
Unpacking org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar (libraries:org.apache.logging.log4j:log4j-core:2.24.1) to libraries/org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar
Unpacking org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar (libraries:org.apache.logging.log4j:log4j-slf4j2-impl:2.24.1) to libraries/org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar
Unpacking org/joml/joml/1.10.8/joml-1.10.8.jar (libraries:org.joml:joml:1.10.8) to libraries/org/joml/joml/1.10.8/joml-1.10.8.jar
Unpacking org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar (libraries:org.lz4:lz4-java:1.8.0) to libraries/org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar
Unpacking org/ow2/asm/asm/9.6/asm-9.6.jar (libraries:org.ow2.asm:asm:9.6) to libraries/org/ow2/asm/asm/9.6/asm-9.6.jar
Unpacking org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar (libraries:org.slf4j:slf4j-api:2.0.16) to libraries/org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar
Starting net.minecraft.server.Main
[18:02:07] [ServerMain/INFO]: Environment: Environment[sessionHost=https://sessionserver.mojang.com, servicesHost=https://api.minecraftservices.com, name=PROD]
[18:02:09] [ServerMain/INFO]: No existing world data, creating new world
[18:02:10] [ServerMain/INFO]: Loaded 1370 recipes
[18:02:10] [ServerMain/INFO]: Loaded 1481 advancements
[18:02:10] [Server thread/INFO]: Starting minecraft server version 1.21.4
[18:02:10] [Server thread/INFO]: Loading properties
[18:02:10] [Server thread/INFO]: Default game type: SURVIVAL
[18:02:10] [Server thread/INFO]: Generating keypair
[18:02:10] [Server thread/INFO]: Starting Minecraft server on *:25565
[18:02:11] [Server thread/INFO]: Using epoll channel type
[18:02:11] [Server thread/INFO]: Preparing level "world"
[18:02:21] [Server thread/INFO]: Preparing start region for dimension minecraft:overworld
[18:02:21] [Worker-Main-3/INFO]: Preparing spawn area: 2%
[18:02:21] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[18:02:22] [Worker-Main-3/INFO]: Preparing spawn area: 2%
[18:02:22] [Worker-Main-3/INFO]: Preparing spawn area: 2%
[18:02:23] [Worker-Main-3/INFO]: Preparing spawn area: 12%
[18:02:23] [Worker-Main-2/INFO]: Preparing spawn area: 18%
[18:02:24] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[18:02:24] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[18:02:25] [Worker-Main-2/INFO]: Preparing spawn area: 28%
[18:02:26] [Worker-Main-2/INFO]: Preparing spawn area: 51%
[18:02:26] [Worker-Main-1/INFO]: Preparing spawn area: 51%
[18:02:26] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[18:02:27] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[18:02:27] [Server thread/INFO]: Time elapsed: 6164 ms
[18:02:27] [Server thread/INFO]: Done (16.414s)! For help, type "help"
[18:02:27] [Server thread/INFO]: Starting remote control listener
[18:02:27] [Server thread/INFO]: Thread RCON Listener started
[18:02:27] [Server thread/INFO]: RCON running on 0.0.0.0:25575
[18:03:27] [Server thread/INFO]: Server empty for 60 seconds, pausing
[18:04:23] [Server thread/INFO]: Stopping the server
[18:04:23] [Server thread/INFO]: Stopping server
[18:04:23] [Server thread/INFO]: Saving players
[18:04:23] [Server thread/INFO]: Saving worlds
[18:04:24] [Server thread/INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:overworld
[18:04:28] [Server thread/INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_end
[18:04:28] [Server thread/INFO]: Saving chunks for level 'ServerLevel[world]'/minecraft:the_nether
[18:04:28] [Server thread/INFO]: ThreadedAnvilChunkStorage (world): All chunks are saved
[18:04:28] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM1): All chunks are saved
[18:04:28] [Server thread/INFO]: ThreadedAnvilChunkStorage (DIM-1): All chunks are saved
[18:04:28] [Server thread/INFO]: ThreadedAnvilChunkStorage: All dimensions are saved
[18:04:28] [Server thread/INFO]: Thread RCON Listener stopped`,
`[Log4jPatcher] [INFO] Transforming org/apache/logging/log4j/core/lookup/JndiLookup
[Log4jPatcher] [INFO] Transforming org/apache/logging/log4j/core/pattern/MessagePatternConverter
[Log4jPatcher] [WARN] Unable to find noLookups:Z field in org/apache/logging/log4j/core/pattern/MessagePatternConverter
Jan 19, 2025 6:36:38 PM io.netty.util.internal.PlatformDependent <clinit>
INFO: Your platform does not provide complete low-level API for accessing direct buffers reliably. Unless explicitly requested, heap buffer will always be preferred to avoid potential system unstability.
[18:36:38] [Server thread/INFO]: Starting minecraft server version 1.7.10
[18:36:38] [Server thread/INFO]: Loading properties
[18:36:38] [Server thread/INFO]: Default game type: SURVIVAL
[18:36:38] [Server thread/INFO]: Generating keypair
[18:36:38] [Server thread/INFO]: Starting Minecraft server on *:25565
[18:36:38] [Server thread/WARN]: Failed to load user banlist:
java.io.FileNotFoundException: banned-players.json (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method) ~[?:?]
at java.base/java.io.FileInputStream.open(Unknown Source) ~[?:?]
at java.base/java.io.FileInputStream.<init>(Unknown Source) ~[?:?]
at com.google.common.io.Files.newReader(Files.java:86) ~[minecraft_server.1.7.10.jar:?]
at om.g(SourceFile:124) ~[minecraft_server.1.7.10.jar:?]
at ls.y(SourceFile:99) [minecraft_server.1.7.10.jar:?]
at ls.<init>(SourceFile:25) [minecraft_server.1.7.10.jar:?]
at lt.e(SourceFile:166) [minecraft_server.1.7.10.jar:?]
at net.minecraft.server.MinecraftServer.run(SourceFile:339) [minecraft_server.1.7.10.jar:?]
at lj.run(SourceFile:628) [minecraft_server.1.7.10.jar:?]
[18:36:38] [Server thread/WARN]: Failed to load ip banlist:
java.io.FileNotFoundException: banned-ips.json (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method) ~[?:?]
at java.base/java.io.FileInputStream.open(Unknown Source) ~[?:?]
at java.base/java.io.FileInputStream.<init>(Unknown Source) ~[?:?]
at com.google.common.io.Files.newReader(Files.java:86) ~[minecraft_server.1.7.10.jar:?]
at om.g(SourceFile:124) ~[minecraft_server.1.7.10.jar:?]
at ls.x(SourceFile:91) [minecraft_server.1.7.10.jar:?]
at ls.<init>(SourceFile:27) [minecraft_server.1.7.10.jar:?]
at lt.e(SourceFile:166) [minecraft_server.1.7.10.jar:?]
at net.minecraft.server.MinecraftServer.run(SourceFile:339) [minecraft_server.1.7.10.jar:?]
at lj.run(SourceFile:628) [minecraft_server.1.7.10.jar:?]
[18:36:38] [Server thread/WARN]: Failed to load operators list:
java.io.FileNotFoundException: ops.json (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method) ~[?:?]
at java.base/java.io.FileInputStream.open(Unknown Source) ~[?:?]
at java.base/java.io.FileInputStream.<init>(Unknown Source) ~[?:?]
at com.google.common.io.Files.newReader(Files.java:86) ~[minecraft_server.1.7.10.jar:?]
at om.g(SourceFile:124) ~[minecraft_server.1.7.10.jar:?]
at ls.z(SourceFile:107) [minecraft_server.1.7.10.jar:?]
at ls.<init>(SourceFile:29) [minecraft_server.1.7.10.jar:?]
at lt.e(SourceFile:166) [minecraft_server.1.7.10.jar:?]
at net.minecraft.server.MinecraftServer.run(SourceFile:339) [minecraft_server.1.7.10.jar:?]
at lj.run(SourceFile:628) [minecraft_server.1.7.10.jar:?]
[18:36:38] [Server thread/WARN]: Failed to load white-list:
java.io.FileNotFoundException: whitelist.json (No such file or directory)
at java.base/java.io.FileInputStream.open0(Native Method) ~[?:?]
at java.base/java.io.FileInputStream.open(Unknown Source) ~[?:?]
at java.base/java.io.FileInputStream.<init>(Unknown Source) ~[?:?]
at com.google.common.io.Files.newReader(Files.java:86) ~[minecraft_server.1.7.10.jar:?]
at om.g(SourceFile:124) ~[minecraft_server.1.7.10.jar:?]
at ls.B(SourceFile:123) [minecraft_server.1.7.10.jar:?]
at ls.<init>(SourceFile:30) [minecraft_server.1.7.10.jar:?]
at lt.e(SourceFile:166) [minecraft_server.1.7.10.jar:?]
at net.minecraft.server.MinecraftServer.run(SourceFile:339) [minecraft_server.1.7.10.jar:?]
at lj.run(SourceFile:628) [minecraft_server.1.7.10.jar:?]
[18:36:38] [Server thread/INFO]: Preparing level "world"
[18:36:38] [Server thread/INFO]: Preparing start region for level 0
[18:36:39] [Server thread/INFO]: Preparing spawn area: 12%
[18:36:40] [Server thread/INFO]: Preparing spawn area: 28%
[18:36:41] [Server thread/INFO]: Preparing spawn area: 48%
[18:36:42] [Server thread/INFO]: Preparing spawn area: 69%
[18:36:43] [Server thread/INFO]: Preparing spawn area: 91%
[18:36:44] [Server thread/INFO]: Done (5.518s)! For help, type "help" or "?"
[18:36:44] [Server thread/INFO]: Starting remote control listener
[18:36:44] [RCON Listener #1/INFO]: RCON running on 0.0.0.0:25575`,
`Unpacking 1.21.4/server-1.21.4.jar (versions:1.21.4) to versions/1.21.4/server-1.21.4.jar
Unpacking com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar (libraries:com.fasterxml.jackson.core:jackson-annotations:2.13.4) to libraries/com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar
Unpacking com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar (libraries:com.fasterxml.jackson.core:jackson-core:2.13.4) to libraries/com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar
Unpacking com/fasterxml/jackson/core/jackson-databind/2.13.4.2/jackson-databind-2.13.4.2.jar (libraries:com.fasterxml.jackson.core:jackson-databind:2.13.4.2) to libraries/com/fasterxml/jackson/core/jackson-databind/2.13.4.2/jackson-databind-2.13.4.2.jar
Unpacking com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar (libraries:com.github.oshi:oshi-core:6.6.5) to libraries/com/github/oshi/oshi-core/6.6.5/oshi-core-6.6.5.jar
Unpacking com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar (libraries:com.github.stephenc.jcip:jcip-annotations:1.0-1) to libraries/com/github/stephenc/jcip/jcip-annotations/1.0-1/jcip-annotations-1.0-1.jar
Unpacking com/google/code/gson/gson/2.11.0/gson-2.11.0.jar (libraries:com.google.code.gson:gson:2.11.0) to libraries/com/google/code/gson/gson/2.11.0/gson-2.11.0.jar
Unpacking com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar (libraries:com.google.guava:failureaccess:1.0.2) to libraries/com/google/guava/failureaccess/1.0.2/failureaccess-1.0.2.jar
Unpacking com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar (libraries:com.google.guava:guava:33.3.1-jre) to libraries/com/google/guava/guava/33.3.1-jre/guava-33.3.1-jre.jar
Unpacking com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar (libraries:com.microsoft.azure:msal4j:1.17.2) to libraries/com/microsoft/azure/msal4j/1.17.2/msal4j-1.17.2.jar
Unpacking com/mojang/authlib/6.0.57/authlib-6.0.57.jar (libraries:com.mojang:authlib:6.0.57) to libraries/com/mojang/authlib/6.0.57/authlib-6.0.57.jar
Unpacking com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar (libraries:com.mojang:brigadier:1.3.10) to libraries/com/mojang/brigadier/1.3.10/brigadier-1.3.10.jar
Unpacking com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar (libraries:com.mojang:datafixerupper:8.0.16) to libraries/com/mojang/datafixerupper/8.0.16/datafixerupper-8.0.16.jar
Unpacking com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar (libraries:com.mojang:jtracy:1.0.29) to libraries/com/mojang/jtracy/1.0.29/jtracy-1.0.29.jar
Unpacking com/mojang/logging/1.5.10/logging-1.5.10.jar (libraries:com.mojang:logging:1.5.10) to libraries/com/mojang/logging/1.5.10/logging-1.5.10.jar
Unpacking com/nimbusds/content-type/2.3/content-type-2.3.jar (libraries:com.nimbusds:content-type:2.3) to libraries/com/nimbusds/content-type/2.3/content-type-2.3.jar
Unpacking com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar (libraries:com.nimbusds:lang-tag:1.7) to libraries/com/nimbusds/lang-tag/1.7/lang-tag-1.7.jar
Unpacking com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar (libraries:com.nimbusds:nimbus-jose-jwt:9.40) to libraries/com/nimbusds/nimbus-jose-jwt/9.40/nimbus-jose-jwt-9.40.jar
Unpacking com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar (libraries:com.nimbusds:oauth2-oidc-sdk:11.18) to libraries/com/nimbusds/oauth2-oidc-sdk/11.18/oauth2-oidc-sdk-11.18.jar
Unpacking commons-io/commons-io/2.17.0/commons-io-2.17.0.jar (libraries:commons-io:commons-io:2.17.0) to libraries/commons-io/commons-io/2.17.0/commons-io-2.17.0.jar
Unpacking io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar (libraries:io.netty:netty-buffer:4.1.115.Final) to libraries/io/netty/netty-buffer/4.1.115.Final/netty-buffer-4.1.115.Final.jar
Unpacking io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar (libraries:io.netty:netty-codec:4.1.115.Final) to libraries/io/netty/netty-codec/4.1.115.Final/netty-codec-4.1.115.Final.jar
Unpacking io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar (libraries:io.netty:netty-common:4.1.115.Final) to libraries/io/netty/netty-common/4.1.115.Final/netty-common-4.1.115.Final.jar
Unpacking io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar (libraries:io.netty:netty-handler:4.1.115.Final) to libraries/io/netty/netty-handler/4.1.115.Final/netty-handler-4.1.115.Final.jar
Unpacking io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar (libraries:io.netty:netty-resolver:4.1.115.Final) to libraries/io/netty/netty-resolver/4.1.115.Final/netty-resolver-4.1.115.Final.jar
Unpacking io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar (libraries:io.netty:netty-transport:4.1.115.Final) to libraries/io/netty/netty-transport/4.1.115.Final/netty-transport-4.1.115.Final.jar
Unpacking io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar (libraries:io.netty:netty-transport-classes-epoll:4.1.115.Final) to libraries/io/netty/netty-transport-classes-epoll/4.1.115.Final/netty-transport-classes-epoll-4.1.115.Final.jar
Unpacking io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar (libraries:io.netty:netty-transport-native-epoll:4.1.115.Final:linux-x86_64) to libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-x86_64.jar
Unpacking io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar (libraries:io.netty:netty-transport-native-epoll:4.1.115.Final:linux-aarch_64) to libraries/io/netty/netty-transport-native-epoll/4.1.115.Final/netty-transport-native-epoll-4.1.115.Final-linux-aarch_64.jar
Unpacking io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar (libraries:io.netty:netty-transport-native-unix-common:4.1.115.Final) to libraries/io/netty/netty-transport-native-unix-common/4.1.115.Final/netty-transport-native-unix-common-4.1.115.Final.jar
Unpacking it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar (libraries:it.unimi.dsi:fastutil:8.5.15) to libraries/it/unimi/dsi/fastutil/8.5.15/fastutil-8.5.15.jar
Unpacking net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar (libraries:net.java.dev.jna:jna:5.15.0) to libraries/net/java/dev/jna/jna/5.15.0/jna-5.15.0.jar
Unpacking net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar (libraries:net.java.dev.jna:jna-platform:5.15.0) to libraries/net/java/dev/jna/jna-platform/5.15.0/jna-platform-5.15.0.jar
Unpacking net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar (libraries:net.minidev:accessors-smart:2.5.1) to libraries/net/minidev/accessors-smart/2.5.1/accessors-smart-2.5.1.jar
Unpacking net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar (libraries:net.minidev:json-smart:2.5.1) to libraries/net/minidev/json-smart/2.5.1/json-smart-2.5.1.jar
Unpacking net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar (libraries:net.sf.jopt-simple:jopt-simple:5.0.4) to libraries/net/sf/jopt-simple/jopt-simple/5.0.4/jopt-simple-5.0.4.jar
Unpacking org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar (libraries:org.apache.commons:commons-lang3:3.17.0) to libraries/org/apache/commons/commons-lang3/3.17.0/commons-lang3-3.17.0.jar
Unpacking org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar (libraries:org.apache.logging.log4j:log4j-api:2.24.1) to libraries/org/apache/logging/log4j/log4j-api/2.24.1/log4j-api-2.24.1.jar
Unpacking org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar (libraries:org.apache.logging.log4j:log4j-core:2.24.1) to libraries/org/apache/logging/log4j/log4j-core/2.24.1/log4j-core-2.24.1.jar
Unpacking org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar (libraries:org.apache.logging.log4j:log4j-slf4j2-impl:2.24.1) to libraries/org/apache/logging/log4j/log4j-slf4j2-impl/2.24.1/log4j-slf4j2-impl-2.24.1.jar
Unpacking org/joml/joml/1.10.8/joml-1.10.8.jar (libraries:org.joml:joml:1.10.8) to libraries/org/joml/joml/1.10.8/joml-1.10.8.jar
Unpacking org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar (libraries:org.lz4:lz4-java:1.8.0) to libraries/org/lz4/lz4-java/1.8.0/lz4-java-1.8.0.jar
Unpacking org/ow2/asm/asm/9.6/asm-9.6.jar (libraries:org.ow2.asm:asm:9.6) to libraries/org/ow2/asm/asm/9.6/asm-9.6.jar
Unpacking org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar (libraries:org.slf4j:slf4j-api:2.0.16) to libraries/org/slf4j/slf4j-api/2.0.16/slf4j-api-2.0.16.jar
Starting net.minecraft.server.Main
[21:40:55] [ServerMain/INFO]: Environment: Environment[sessionHost=https://sessionserver.mojang.com, servicesHost=https://api.minecraftservices.com, name=PROD]
[21:40:59] [ServerMain/INFO]: No existing world data, creating new world
[21:41:01] [ServerMain/INFO]: Loaded 1370 recipes
[21:41:01] [ServerMain/INFO]: Loaded 1481 advancements
[21:41:01] [Server thread/INFO]: Starting minecraft server version 1.21.4
[21:41:01] [Server thread/INFO]: Loading properties
[21:41:01] [Server thread/INFO]: Default game type: SURVIVAL
[21:41:01] [Server thread/INFO]: Generating keypair
[21:41:01] [Server thread/INFO]: Starting Minecraft server on *:25565
[21:41:02] [Server thread/INFO]: Using epoll channel type
[21:41:02] [Server thread/INFO]: Preparing level "world"
[21:41:22] [Server thread/INFO]: Preparing start region for dimension minecraft:overworld
[21:41:23] [Worker-Main-2/INFO]: Preparing spawn area: 2%
[21:41:23] [Worker-Main-2/INFO]: Preparing spawn area: 2%
[21:41:24] [Worker-Main-2/INFO]: Preparing spawn area: 2%
[21:41:24] [Worker-Main-3/INFO]: Preparing spawn area: 2%
[21:41:24] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:25] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:25] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:26] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:26] [Worker-Main-3/INFO]: Preparing spawn area: 2%
[21:41:27] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:27] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:28] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:28] [Worker-Main-1/INFO]: Preparing spawn area: 2%
[21:41:29] [Worker-Main-3/INFO]: Preparing spawn area: 8%
[21:41:30] [Worker-Main-2/INFO]: Preparing spawn area: 18%
[21:41:30] [Worker-Main-2/INFO]: Preparing spawn area: 18%
[21:41:30] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[21:41:31] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[21:41:31] [Worker-Main-3/INFO]: Preparing spawn area: 18%
[21:41:32] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[21:41:32] [Worker-Main-1/INFO]: Preparing spawn area: 18%
[21:41:33] [Worker-Main-3/INFO]: Preparing spawn area: 18%
[21:41:33] [Worker-Main-3/INFO]: Preparing spawn area: 18%
[21:41:34] [Worker-Main-3/INFO]: Preparing spawn area: 18%
[21:41:34] [Worker-Main-3/INFO]: Preparing spawn area: 20%
[21:41:35] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:35] [Worker-Main-1/INFO]: Preparing spawn area: 51%
[21:41:36] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:36] [Worker-Main-2/INFO]: Preparing spawn area: 51%
[21:41:37] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:37] [Worker-Main-1/INFO]: Preparing spawn area: 51%
[21:41:38] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:38] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:39] [Worker-Main-3/INFO]: Preparing spawn area: 51%
[21:41:39] [Worker-Main-2/INFO]: Preparing spawn area: 51%
[21:41:40] [Server thread/INFO]: Time elapsed: 17596 ms
[21:41:40] [Server thread/INFO]: Done (38.008s)! For help, type "help"
[21:41:40] [Server thread/INFO]: Starting remote control listener
[21:41:40] [Server thread/INFO]: Thread RCON Listener started
[21:41:40] [Server thread/INFO]: RCON running on 0.0.0.0:25575
[21:42:17] [User Authenticator #1/INFO]: UUID of player Sirherobrine23 is 0dc9df8f-9f5a-45d8-8848-9262a4357ae0
[21:42:20] [Server thread/INFO]: Sirherobrine23[/[0:0:0:0:0:0:0:1]:54662] logged in with entity id 41 at (37.5, 76.0, -57.5)
[21:42:20] [Server thread/INFO]: Sirherobrine23 joined the game
[21:43:42] [Server thread/WARN]: Sirherobrine23 moved too quickly! 9.663497831602484,3.176759275064242,0.8707508869438136
[21:43:42] [Server thread/WARN]: Can't keep up! Is the server overloaded? Running 4510ms or 90 ticks behind
[21:45:18] [Server thread/INFO]: Sirherobrine23 lost connection: Disconnected
[21:45:18] [Server thread/INFO]: Sirherobrine23 left the game`,
}
)

View File

@ -1,178 +0,0 @@
package mclog
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"errors"
"io"
"io/fs"
"net/http"
"net/url"
"slices"
"strconv"
"strings"
"time"
)
type Storage interface {
fs.FS
Remove(name string) error // Remove file from storage
Create(name string) (io.WriteCloser, error) // Save file in storage
}
func writeJSON(w io.Writer, obj any) {
enc := json.NewEncoder(w)
enc.SetIndent("", " ")
enc.Encode(obj)
}
func writeErr(w http.ResponseWriter, err error) {
w.Header().Set("Content-Type", "application/json")
if errors.Is(err, fs.ErrNotExist) {
w.WriteHeader(404)
writeJSON(w, MclogResponseStatus{Success: false, ErrorMessage: ErrNoExists.Error()})
return
}
w.WriteHeader(500)
writeJSON(w, MclogResponseStatus{Success: false, ErrorMessage: err.Error()})
}
func NewHandler(serverLimits Limits, storage Storage) *http.ServeMux {
control := http.NewServeMux()
// Return server limits to process logs
control.HandleFunc("GET /1/limits", func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(200)
writeJSON(w, Limits{
StorageTime: serverLimits.StorageTime / time.Second, // return only seconds
MaxLength: serverLimits.MaxLength,
MaxLines: serverLimits.MaxLines,
})
})
// Get log stream
control.HandleFunc("GET /1/raw/{id}", func(w http.ResponseWriter, r *http.Request) {
logId := r.PathValue("id")
body, err := storage.Open(logId)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
w.WriteHeader(404)
writeJSON(w, MclogResponseStatus{Success: false, ErrorMessage: ErrNoExists.Error()})
return
}
w.WriteHeader(500)
writeJSON(w, MclogResponseStatus{Success: false, ErrorMessage: err.Error()})
return
}
defer body.Close()
w.Header().Set("Content-Type", "text/plain") // Set file stream
if stat, err := body.Stat(); err == nil {
w.Header().Set("content-length", strconv.FormatInt(stat.Size(), 10))
}
// Response
w.WriteHeader(200)
go io.Copy(w, body)
})
control.HandleFunc("GET /1/insights/{id}", func(w http.ResponseWriter, r *http.Request) {
logId := r.PathValue("id") // get log id
// Open log file
logBody, err := storage.Open(logId)
if err != nil {
writeErr(w, err)
return
}
defer logBody.Close()
// Parse log body
logInsight, err := ParseLog(logBody);
if err != nil {
writeErr(w, err)
return
}
// return log insight
w.WriteHeader(200)
writeJSON(w, logInsight)
})
control.HandleFunc("POST /1/log", func(w http.ResponseWriter, r *http.Request) {
// Close body after run function
defer r.Body.Close()
// Set only reader type
logStream := io.Reader(r.Body)
if serverLimits.MaxLength > 0 {
logStream = io.LimitReader(r.Body, serverLimits.MaxLength) // Set max body lenght from limits
}
switch {
case slices.Contains(r.Header.Values("Content-Type"), "application/octet-stream"):
// noop, only reader body from logStream
case slices.Contains(r.Header.Values("Content-Type"), "application/x-www-form-urlencoded"):
// Read all body
body, err := io.ReadAll(logStream)
if err != nil {
writeErr(w, err)
return
}
// Parse body
form, err := url.ParseQuery(string(body))
if err != nil {
writeErr(w, err)
return
}
// Check if body have "content"
if !form.Has("content") {
w.WriteHeader(400)
writeJSON(w, MclogResponseStatus{Success: false, ErrorMessage: "require the 'content' in body"})
return
}
// Attemp write log file
logStream = strings.NewReader(form.Get("content"))
default:
w.WriteHeader(400)
writeJSON(w, MclogResponseStatus{Success: false, ErrorMessage: "Require 'application/x-www-form-urlencoded' or raw stream/'application/octet-stream'"})
return
}
var randomId string
for randomId == "" {
buff := make([]byte, 8)
if _, err := rand.Read(buff); err != nil {
writeErr(w, err)
return
}
randomId = hex.EncodeToString(buff)
if f, err := storage.Open(randomId); err == nil {
f.Close() // Close file
randomId = ""
}
}
// Attemp write log file
file, err := storage.Create(randomId)
if err != nil {
writeErr(w, err)
return
}
defer file.Close()
if _, err = io.Copy(file, logStream); err != nil {
writeErr(w, err)
return
}
// Write success log id
w.WriteHeader(201)
writeJSON(w, MclogResponseStatus{Success: true, Id: randomId})
})
return control
}