11package api
22
33import (
4- "net/http"
5-
4+ "archive/zip"
5+ "bytes"
6+ "encoding/json"
7+ "encoding/xml"
8+ "errors"
9+ "io"
610 "meteor-server/pkg/core"
711 "meteor-server/pkg/db"
12+ "net/http"
13+ "strconv"
14+ "strings"
15+ "sync"
16+ "time"
817)
918
1019type Stats struct {
@@ -15,6 +24,22 @@ type Stats struct {
1524 Downloads int `json:"downloads"`
1625 OnlinePlayers int `json:"onlinePlayers"`
1726 OnlineUUIDs int `json:"onlineUUIDs"`
27+
28+ Builds map [string ]int `json:"builds"`
29+ }
30+
31+ var builds map [string ]int
32+
33+ func InitStats () {
34+ t := time .NewTicker (time .Minute )
35+
36+ go func () {
37+ for {
38+ builds = getBuildNumbers ()
39+
40+ <- t .C
41+ }
42+ }()
1843}
1944
2045func StatsHandler (w http.ResponseWriter , r * http.Request ) {
@@ -23,7 +48,14 @@ func StatsHandler(w http.ResponseWriter, r *http.Request) {
2348 if date == "" {
2449 g := db .GetGlobal ()
2550
26- core .Json (w , Stats {Config : core .GetConfig (), Date : core .GetDate (), DevBuild : g .DevBuild , Downloads : g .Downloads , OnlinePlayers : GetPlayingCount ()})
51+ core .Json (w , Stats {
52+ Config : core .GetConfig (),
53+ Date : core .GetDate (),
54+ DevBuild : g .DevBuild ,
55+ Downloads : g .Downloads ,
56+ OnlinePlayers : GetPlayingCount (),
57+ Builds : builds ,
58+ })
2759 } else {
2860 stats , err := db .GetJoinStats (date )
2961
@@ -35,3 +67,147 @@ func StatsHandler(w http.ResponseWriter, r *http.Request) {
3567 core .Json (w , stats )
3668 }
3769}
70+
71+ func RecheckMavenHandler (w http.ResponseWriter , _ * http.Request ) {
72+ builds = getBuildNumbers ()
73+
74+ core .Json (w , struct {}{})
75+ }
76+
77+ // Maven
78+
79+ type MavenSnapshotVersion struct {
80+ Extension string `xml:"extension"`
81+ Classifier string `xml:"classifier"`
82+ Value string `xml:"value"`
83+ }
84+
85+ type MavenVersioning struct {
86+ Versions []string `xml:"versions>version"`
87+ SnapshotVersions []MavenSnapshotVersion `xml:"snapshotVersions>snapshotVersion"`
88+ }
89+
90+ type MavenMetadata struct {
91+ Versioning MavenVersioning `xml:"versioning"`
92+ }
93+
94+ type FabricMod struct {
95+ Version string `json:"version"`
96+ }
97+
98+ func getBuildNumbers () map [string ]int {
99+ builds := make (map [string ]int )
100+
101+ res , err := http .Get ("https://maven.meteordev.org/snapshots/meteordevelopment/meteor-client/maven-metadata.xml" )
102+ if err != nil {
103+ return builds
104+ }
105+
106+ var metadata MavenMetadata
107+ err = xml .NewDecoder (res .Body ).Decode (& metadata )
108+ if err != nil {
109+ return builds
110+ }
111+
112+ mutex := sync.Mutex {}
113+ group := sync.WaitGroup {}
114+
115+ for _ , version := range metadata .Versioning .Versions {
116+ version := version
117+ group .Add (1 )
118+
119+ go func () {
120+ i := strings .IndexRune (version , '-' )
121+ mcVersion := version [:i ]
122+
123+ if ! strings .HasPrefix (mcVersion , "0" ) {
124+ build , err := getBuildNumber (version )
125+
126+ if err == nil {
127+ mutex .Lock ()
128+ builds [mcVersion ] = build
129+ mutex .Unlock ()
130+ }
131+ }
132+
133+ group .Done ()
134+ }()
135+ }
136+
137+ group .Wait ()
138+
139+ return builds
140+ }
141+
142+ func getBuildNumber (version string ) (int , error ) {
143+ res , err := http .Get ("https://maven.meteordev.org/snapshots/meteordevelopment/meteor-client/" + version + "/maven-metadata.xml" )
144+ if err != nil {
145+ return - 1 , err
146+ }
147+
148+ //goland:noinspection GoUnhandledErrorResult
149+ defer res .Body .Close ()
150+
151+ var metadata MavenMetadata
152+ err = xml .NewDecoder (res .Body ).Decode (& metadata )
153+ if err != nil {
154+ return - 1 , err
155+ }
156+
157+ var filename = ""
158+
159+ for _ , version := range metadata .Versioning .SnapshotVersions {
160+ if version .Classifier == "" && version .Extension == "jar" {
161+ filename = "meteor-client-" + version .Value + ".jar"
162+ break
163+ }
164+ }
165+
166+ res , err = http .Get ("https://maven.meteordev.org/snapshots/meteordevelopment/meteor-client/" + version + "/" + filename )
167+ if err != nil {
168+ return - 1 , err
169+ }
170+
171+ //goland:noinspection GoUnhandledErrorResult
172+ defer res .Body .Close ()
173+
174+ body , err := io .ReadAll (res .Body )
175+ if err != nil {
176+ return - 1 , err
177+ }
178+
179+ jar , err := zip .NewReader (bytes .NewReader (body ), int64 (len (body )))
180+ if err != nil {
181+ return - 1 , err
182+ }
183+
184+ var mod FabricMod
185+
186+ for _ , file := range jar .File {
187+ if file .Name == "fabric.mod.json" {
188+ reader , err := file .Open ()
189+ if err != nil {
190+ continue
191+ }
192+
193+ _ = json .NewDecoder (reader ).Decode (& mod )
194+
195+ _ = reader .Close ()
196+
197+ break
198+ }
199+ }
200+
201+ if mod .Version != "" {
202+ i := strings .IndexRune (mod .Version , '-' )
203+
204+ build , err := strconv .ParseInt (mod .Version [i + 1 :], 10 , 32 )
205+ if err != nil {
206+ return - 1 , err
207+ }
208+
209+ return int (build ), nil
210+ }
211+
212+ return - 1 , errors .New ("unknown build" )
213+ }
0 commit comments