33import net .minecraft .server .network .ServerPlayerEntity ;
44import net .minecraft .text .Text ;
55import top .gregtao .concerto .ConcertoServer ;
6+ import top .gregtao .concerto .api .DynamicPath ;
7+ import top .gregtao .concerto .http .HttpURLInputStream ;
68import top .gregtao .concerto .music .Music ;
9+ import top .gregtao .concerto .music .SharedMusic ;
10+ import top .gregtao .concerto .player .MusicPlayer ;
711
812import java .util .ArrayList ;
913import java .util .List ;
@@ -28,6 +32,9 @@ public class ServerMusicAgent {
2832 private ScheduledFuture <?> playNextFuture ;
2933 private final ConcurrentLinkedQueue <Music > musicQueue = new ConcurrentLinkedQueue <>();
3034 private Music currentMusic = null ;
35+ private Music currentSharedMusic = null ;
36+ private int totalBytes = 0 ;
37+ private long playTime = 0 ;
3138
3239 private final AtomicBoolean isPlaying = new AtomicBoolean (false );
3340
@@ -84,42 +91,68 @@ private synchronized void endVoting() {
8491 ConcertoServer .LOGGER .info ("Vote: Keep current music" );
8592 }
8693 Text text = success ? Text .translatable ("concerto.agent.vote.success" ) : Text .translatable ("concerto.agent.vote.failed" );
87- this .members . forEach ( player -> player . sendMessage ( text , false ) );
94+ this .broadcast ( text );
8895
8996 this .isVoting = false ;
9097 }
9198
9299 private synchronized void playNextMusic () {
93100 this .currentMusic = this .musicQueue .poll ();
101+ this .currentSharedMusic = null ;
102+ this .totalBytes = 0 ;
103+ this .playTime = 0 ;
94104 if (this .currentMusic == null ) {
95105 ConcertoServer .LOGGER .info ("Music agent paused" );
96106 this .isPlaying .set (false );
97107 } else {
98108 ConcertoServer .LOGGER .info ("Start playing music {}" , this .currentMusic .getMeta ().title ());
109+ if (this .currentMusic instanceof DynamicPath dynamicPath ) {
110+ String path = dynamicPath .getLastRawPath ();
111+ if (path != null ) this .totalBytes = HttpURLInputStream .getTotalBytes (path );
112+ else {
113+ ConcertoServer .LOGGER .warn ("Cannot play music {}" , this .currentMusic .getMeta ().title ());
114+ this .broadcast (Text .translatable ("concerto.agent.play.failed" ));
115+ this .playNextFuture = this .musicScheduler .schedule (this ::playNextMusic , 1 , TimeUnit .SECONDS );
116+ return ;
117+ }
118+ this .currentSharedMusic = new SharedMusic (path , this .currentMusic .getMeta (),
119+ dynamicPath .getLastLyrics (), dynamicPath .getLastSubLyrics ());
120+ } else {
121+ this .currentSharedMusic = this .currentMusic ;
122+ }
99123 this .isPlaying .set (true );
100- ServerMusicNetworkHandler .musicAgentSendMusic (this .members , this .currentMusic , 0 );
124+ this .playTime = System .currentTimeMillis ();
125+ ServerMusicNetworkHandler .musicAgentSendMusic (this .members , this .currentSharedMusic , 0 );
101126 this .playNextFuture = this .musicScheduler .schedule (this ::playNextMusic ,
102127 this .currentMusic .getMeta ().getDuration ().asSeconds (), TimeUnit .SECONDS );
103128 }
104129 }
105130
131+ public void broadcast (Text text ) {
132+ this .members .forEach (player -> player .sendMessage (text , false ));
133+ }
134+
106135 public synchronized boolean isMember (ServerPlayerEntity player ) {
107136 return this .members .contains (player );
108137 }
109138
110139 public synchronized void addMusic (Music music ) {
111- ConcertoServer .LOGGER .info ("Added music {}" , music .getMeta ().title ());
112- this .musicQueue .offer (music );
113- if (!this .isPlaying .get ()) {
114- this .playNextFuture = this .musicScheduler .schedule (this ::playNextMusic , 1 , TimeUnit .SECONDS );
115- }
140+ MusicPlayer .run (() -> {
141+ ConcertoServer .LOGGER .info ("Added music {}" , music .getMeta ().title ());
142+ this .musicQueue .offer (music );
143+ if (!this .isPlaying .get ()) {
144+ this .playNextFuture = this .musicScheduler .schedule (this ::playNextMusic , 1 , TimeUnit .SECONDS );
145+ }
146+ });
116147 }
117148
118149 public synchronized void playerJoin (ServerPlayerEntity player ) {
119150 ConcertoServer .LOGGER .info ("Player {} joined music agent" , player .getName ().getString ());
120151 this .members .add (player );
121- if (this .isPlaying .get () && this .currentMusic != null ) {
122- ServerMusicNetworkHandler .musicAgentSendMusic (this .members , this .currentMusic , 0 );
152+ if (this .isPlaying .get () && this .currentSharedMusic != null ) {
153+ ServerMusicNetworkHandler .musicAgentSendMusic (player , this .currentSharedMusic ,
154+ this .totalBytes * (System .currentTimeMillis () - this .playTime ) /
155+ this .currentMusic .getMeta ().getDuration ().asMilliseconds ());
123156 }
124157 }
125158
@@ -141,7 +174,9 @@ public synchronized void reset() {
141174 this .playNextFuture .cancel (false );
142175 }
143176 this .musicQueue .clear ();
144- this .currentMusic = null ;
177+ this .currentMusic = this .currentSharedMusic = null ;
178+ this .totalBytes = 0 ;
179+ this .playTime = 0 ;
145180 this .isPlaying .set (false );
146181
147182 ConcertoServer .LOGGER .info ("Reset server music agent" );
0 commit comments