Update to Eaglercraft 1.5 sp2

This commit is contained in:
eaglercraft 2024-12-14 14:18:36 -08:00
parent 7a8275c055
commit 9761916953
14 changed files with 61415 additions and 72051 deletions

Binary file not shown.

View File

@ -1,4 +1,15 @@
§lCHANGES §lIN §lSERVICE §lPACK §l#2:§r
- improved resource packs §4(by §4ayunami2000)§r
- improved the FPS even more
- improved voice chat reliability
- improved shared worlds reliability
- backported X's vanilla world export
- backported X's vsync mode
Release date: §912/14/2024§r
§lCHANGES §lIN §lSERVICE §lPACK §l#1:§r §lCHANGES §lIN §lSERVICE §lPACK §l#1:§r
- resource packs §4(by §4ayunami2000)§r - resource packs §4(by §4ayunami2000)§r

View File

@ -31,8 +31,8 @@ menu.generatingTerrain=Building terrain
menu.convertingLevel=Converting world menu.convertingLevel=Converting world
menu.simulating=Simulating the world for a bit menu.simulating=Simulating the world for a bit
menu.respawning=Respawning menu.respawning=Respawning
menu.shareToLan=Open to LAN menu.shareToLan=Invite
menu.closeLan=Close LAN menu.closeLan=Stop Sharing
menu.skinCapeSettings=Skins/Capes Settings menu.skinCapeSettings=Skins/Capes Settings
menu.skinCapeSettingsNote0=I put the button up here so menu.skinCapeSettingsNote0=I put the button up here so
@ -250,15 +250,15 @@ addServer.enterName=Server Name
addServer.enterIp=Server Address addServer.enterIp=Server Address
addServer.add=Done addServer.add=Done
addServer.hideAddress=Hide Address addServer.hideAddress=Hide Address
lanServer.title=LAN World lanServer.title=Shared World
lanServer.scanning=Scanning for games on your local network lanServer.scanning=Scanning for games on your local network
lanServer.start=Start LAN World lanServer.start=Start Shared World
lanServer.otherPlayers=Settings for Other Players lanServer.otherPlayers=Settings for Other Players
mcoServer.title=yeeeeeee mcoServer.title=yeeeeeee
addServer.SSLWarn1=you are on an https: page! addServer.SSLWarn1=you are on an https: page!
addServer.SSLWarn2=html5 will only allow wss:// addServer.SSLWarn2=html5 will only allow wss://
directConnect.prompt=What would you like to do? directConnect.prompt=What would you like to do?
directConnect.lanWorld=Join LAN World directConnect.lanWorld=Join Shared World
directConnect.lanWorldCode=Enter Join Code: directConnect.lanWorldCode=Enter Join Code:
directConnect.networkSettingsNote=Click 'Network Settings' to add a relay URL directConnect.networkSettingsNote=Click 'Network Settings' to add a relay URL
directConnect.ipGrabNote=Note: The world's owner can get your IP address directConnect.ipGrabNote=Note: The world's owner can get your IP address
@ -266,7 +266,7 @@ directConnect.serverJoin=Connect to Server
directConnect.lanWorldJoin=Join World directConnect.lanWorldJoin=Join World
directConnect.lanWorldRelay=Network Settings directConnect.lanWorldRelay=Network Settings
lanServer.pauseMenu0=Sharing to LAN lanServer.pauseMenu0=Sharing World
lanServer.pauseMenu1=Relay URL: lanServer.pauseMenu1=Relay URL:
lanServer.pauseMenu2=Join Code: lanServer.pauseMenu2=Join Code:
@ -276,13 +276,13 @@ lanServer.worldName=World Name:
lanServer.hidden=Hidden: lanServer.hidden=Hidden:
lanServer.hideCode=hide details lanServer.hideCode=hide details
lanServer.showCode=show details lanServer.showCode=show details
lanServer.opened=LAN world opened on $relay$, join code is §a$code$ lanServer.opened=Shared world opened on $relay$, join code is §a$code$
lanServer.closed=LAN world closed lanServer.closed=Shared world closed
lanServer.pleaseWait=Please Wait... lanServer.pleaseWait=Please Wait...
lanServer.relayDisconnected=Error: connection to LAN relay was lost, you must re-share the world to invide more people lanServer.relayDisconnected=Error: connection to shared world relay was lost, you must re-share the world to invide more people
lanServer.ipGrabNote=Note: Players joining your world can get your IP address lanServer.ipGrabNote=Note: Players joining your world can get your IP address
networkSettings.title=LAN World Relay Servers networkSettings.title=Shared World Relay Servers
networkSettings.add=Add Relay networkSettings.add=Add Relay
networkSettings.delete=Delete Relay networkSettings.delete=Delete Relay
networkSettings.default=Set Primary networkSettings.default=Set Primary
@ -492,6 +492,18 @@ performance.max=Max FPS
performance.balanced=Balanced performance.balanced=Balanced
performance.powersaver=Power saver performance.powersaver=Power saver
options.vsyncWarning.title=Issues Detected
options.vsyncWarning.0=Some of your video settings may be causing
options.vsyncWarning.1=the game to lag excessively
options.vsyncWarning.2=VSync is disabled, some browsers require
options.vsyncWarning.3=VSync to be enabled to hint when the
options.vsyncWarning.4=framebuffer has updated. If the game feels
options.vsyncWarning.5=significantly slower than is indicated by
options.vsyncWarning.6=the FPS counter, you should enable VSync.
options.vsyncWarning.fixSettings=Fix Settings
options.vsyncWarning.continueAnyway=Continue Anyway
options.vsyncWarning.doNotShowAgain=Do Not Show Again
controls.title=Controls controls.title=Controls
key.forward=Forward key.forward=Forward

File diff suppressed because one or more lines are too long

View File

@ -1,33 +1,30 @@
# Eaglercraft 1.5.2 - Service Pack 1 # Eaglercraft 1.5.2 - Service Pack 2
### Released: October 27th, 2023 - "23w43a" ### Released: December 14th, 2024 - "24w50a"
###
![eaglercraft](https://g.deev.is/eaglercraft/cover.png) ![eaglercraft](https://g.deev.is/eaglercraft/cover.png)
:-: :-:
Eaglercraft is real Minecraft 1.5.2 that you can play in any regular web browser. That includes school chromebooks, it works on all chromebooks. It supports both singleplayer and multiplayer. Eaglercraft is real Minecraft 1.5.2 that you can play in any regular web browser. That includes school chromebooks, it works on all chromebooks. It supports both singleplayer and multiplayer.
# New in Service Pack 1: # New in Service Pack 2:
**Huge FPS boosts, lessons learned during the development of EaglercraftX 1.8 have been applied to 1.5, and [ayunami2000](https://github.com/ayunami2000/) added basic support for resource packs. Update your unblocked game websites ASAP for the best user experience on slow hardware.** - improved resource packs (by ayunami2000)
- fixed sunrise/sunset fog color - improved the FPS even more
- better optimized pipeline shader - improved voice chat reliability
- backported X's state management - improved shared worlds reliability
- backported X's buffer streaming - backported X's vanilla world export
- backported X's hotbar FPS fix - backported X's vsync mode
- backported X's double buffering
- backported X's FXAA shader
**The LAN world relay server is now bundled with the client, you can download it using a button in the "Network Settings" menu where LAN relays are configured. Hopefully this will prevent LAN worlds from ever becoming defunct once our default relay servers no longer exist.** **The updated shared world relay server is bundled with the client, you can download it using a button in the "Network Settings" menu where shared world relays are configured. Hopefully this will prevent shared worlds from ever becoming defunct once our default relay servers no longer exist.**
# Table Of Contents: # Table Of Contents:
| [Singleplayer](#Singleplayer) | [Multiplayer](#Multiplayer) | [Others](#Others) | | [Singleplayer](#Singleplayer) | [Multiplayer](#Multiplayer) | [Others](#Others) |
|---------------------------------------------------------------|---------------------------------------------------------------------------------|-------------------------------------------------------| |---------------------------------------------------------------|---------------------------------------------------------------------------------|-------------------------------------------------------|
| [Importing and Exporting Worlds](#Importing-and-Exporting-Worlds) | [Public clients and servers](#Public-clients-and-servers) | [Plugin Development](#Plugin-Development) | | [Importing and Exporting Worlds](#Importing-and-Exporting-Worlds) | [Public clients and servers](#Public-clients-and-servers) | [Plugin Development](#Plugin-Development) |
| [LAN Worlds](#LAN-Worlds) | [Creating a Server - Bukkit](#Creating-a-server---Bukkit) | [Compiling](#Compiling) | | [Shared Worlds](#Shared-Worlds) | [Creating a Server - Bukkit](#Creating-a-server---Bukkit) | [Compiling](#Compiling) |
| [Public LAN Relays](#Public-LAN-Relays) | [Creating a Server - EaglercraftBungee](#Creating-a-server---EaglercraftBungee) | [Creating a resource pack](#Creating-a-resource-pack) | | [Public Shared World Relays](#Public-Shared-World-Relays) | [Creating a Server - EaglercraftBungee](#Creating-a-server---EaglercraftBungee) | [Creating a resource pack](#Creating-a-resource-pack) |
| [Creating a LAN Relay](#Creating-a-LAN-Relay) | [Creating a Client](#Creating-a-Client) | [Contributing](#Contributing) | | [Creating a Shared World Relay](#Creating-a-Shared-World-Relay) | [Creating a Client](#Creating-a-Client) | [Contributing](#Contributing) |
| | [EaglercraftBungee Configuration](#EaglercraftBungee-Configuration) | | | | [EaglercraftBungee Configuration](#EaglercraftBungee-Configuration) | |
| | [Creating a Reverse Proxy - NGINX](#Creating-a-Reverse-Proxy---NGINX) | | | | [Creating a Reverse Proxy - NGINX](#Creating-a-Reverse-Proxy---NGINX) | |
| | [NGINX Configuration](#NGINX-Configuration) | | | | [NGINX Configuration](#NGINX-Configuration) | |
@ -39,21 +36,21 @@ Simply press the 'Singleplayer' button on the main menu and you can create a reg
## Importing and Exporting Worlds ## Importing and Exporting Worlds
The worlds are stored in your browser's local storage, **you can export them as EPK files and import them again on all other Eaglercraft sites that also support singleplayer.** You can even copy an exported world to an entirely different computer, or send it to a friend, and import it and continue playing with all your progress saved. The worlds are stored in your browser's local storage, **you can export them as EPK files and import them again on all other Eaglercraft sites that also support singleplayer.** You can even copy an exported world to an entirely different computer, or send it to a friend, and import it and continue playing with all your progress saved.
## LAN Worlds ## Shared Worlds
### Eaglercraft fully supports LAN worlds, you can share your world with any player and they can connect directly to it as if you are running a server in your browser. ### Eaglercraft fully supports shared worlds, you can share your world with any player and they can connect directly to it as if you are running a server in your browser.
**LAN worlds will work between any two devices connected to the internet, you are not limited to only players connected to your Wi-Fi network** ### This feature was originally called "LAN Worlds" but has been renamed to "Shared Worlds" to avoid confusing people who believed it only works if both devices are on the same Wi-Fi network
To open your world to LAN, go to the pause menu and click 'Open to LAN'. You can configure the gamemode and cheats and if you would like to hide your LAN world. **When you do not hide your LAN world, it will appear on the Multiplayer screen from the main menu to anybody else also on your Wi-Fi network.** Set the world hidden if you are at school or something and don't want everyone else in your class to join as well and start griefing. To invite players to your world, go to the pause menu and click 'Invite'. You can configure the gamemode and cheats and if you would like to hide your world from others. **When you do not hide your shared world, it will appear on the Multiplayer screen from the main menu to anybody else also on your Wi-Fi network.** Set the world hidden if you are at school or something and don't want everyone else in your class to join as well and start griefing.
When you open the world to LAN it will give you a 'join code'. Simply share the code with your friends and they can visit the Multiplayer screen from the main menu and click 'Direct Connect' and enter the code and they will be able to join your world. When you share the world it will give you a 'join code'. Simply share the code with your friends and they can visit the Multiplayer screen from the main menu and click 'Direct Connect' and enter the code and they will be able to join your world.
Make sure they add the relay server your game opens the LAN world on to their "Network Settings" menu accessable from the Multiplayer screen. You simply must send them the URL indicated in the pause menu once the world is opened and they can use the "Add Relay" option to add the URL to their list. Make sure they add the relay server your game opens the shred world on to their "Network Settings" menu accessable from the Multiplayer screen. You simply must send them the URL indicated in the pause menu once the world is opened and they can use the "Add Relay" option to add the URL to their list.
### THIS IS A REQUIRED STEP FOR A PERSON TO JOIN YOUR WORLD, IF THEY DO NOT HAVE THE RELAY YOUR WORLD IS HOSTED ON ADDED TO THEIR "Network Settings" THE GAME WILL BE UNABLE TO LOCATE THE WORLD ### THIS IS A REQUIRED STEP FOR A PERSON TO JOIN YOUR WORLD, IF THEY DO NOT HAVE THE RELAY YOUR WORLD IS HOSTED ON ADDED TO THEIR "Network Settings" THE GAME WILL BE UNABLE TO LOCATE THE WORLD
## Public LAN Relays ## Public Shared World Relays
### Here are some public relay servers you can use: ### Here are some public relay servers you can use:
@ -61,13 +58,13 @@ Make sure they add the relay server your game opens the LAN world on to their "N
- `wss://relay.lax1dude.net/` - `wss://relay.lax1dude.net/`
- `wss://relay.shhnowisnottheti.me/` - `wss://relay.shhnowisnottheti.me/`
## Creating a LAN Relay ## Creating a Shared World Relay
### Simply download [stable-download/sp-relay.jar](https://github.com/lax1dude/eaglercraft/blob/main/stable-download/sp-relay.jar) and run `java -jar sp-relay.jar` ### The source code of the shared world relay has been moved to the EaglercraftX repository, however a copy of the compiled JAR file is embedded in every client and can be downloaded from the "Network Settings" screen.
**Run `java -jar sp-relay.jar --debug` to view debug info like all the IPs of incoming connections, as it is not shown by default because logging all that info will reduce performance when the relay is being pinged many times a second depending on it's popularity.** **Run `java -jar EaglerSPRelay.jar --debug` to view debug info like all the IPs of incoming connections, as it is not shown by default because logging all that info will reduce performance when the relay is being pinged many times a second depending on it's popularity.**
Edit the `relayConfig.ini` file generated on first launch to change the port and configure ratelimiting and such, and `relays.txt` to change the list of STUN and TURN relays reported to clients connecting to the relay, which are required to correctly establish a P2P LAN world connection in browsers Edit the `relayConfig.ini` file generated on first launch to change the port and configure ratelimiting and such, and `relays.txt` to change the list of STUN and TURN relays reported to clients connecting to the relay, which are required to correctly establish a P2P connection in browsers
**The `origin-whitelist` config variable is a semicolon (`;`) seperated list of domains used to restrict what sites are to be allowed to use your relay. When left blank it allows all sites. Add `offline` to allow offline download clients to use your relay as well, and `null` to allow connections that do not specify an `Origin:` header. Use `*` as a wildcard, for example: `*.deev.is` allows all domains ending with "deev.is" to use the relay.** **The `origin-whitelist` config variable is a semicolon (`;`) seperated list of domains used to restrict what sites are to be allowed to use your relay. When left blank it allows all sites. Add `offline` to allow offline download clients to use your relay as well, and `null` to allow connections that do not specify an `Origin:` header. Use `*` as a wildcard, for example: `*.deev.is` allows all domains ending with "deev.is" to use the relay.**

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,29 +1,26 @@
window.initializeVoiceClient=()=>{class k{constructor(a,b,c,f){this.client=a;this.peerId=b;this.peerConnection=c;this.stream=null;const e=this;this.peerConnection.addEventListener("icecandidate",g=>{g.candidate&&e.client.iceCandidateHandler(e.peerId,JSON.stringify({sdpMLineIndex:g.candidate.sdpMLineIndex,candidate:g.candidate.candidate}))});this.peerConnection.addEventListener("track",g=>{e.rawStream=g.streams[0];const h=new Audio;h.autoplay=!0;h.muted=!0;h.onended=function(){h.remove()};h.srcObject= window.initializeVoiceClient=()=>{class k{constructor(a,b,c,e){this.client=a;this.peerId=b;this.peerConnection=c;this.stream=null;this.peerConnection.addEventListener("icecandidate",f=>{f.candidate&&this.client.iceCandidateHandler(this.peerId,JSON.stringify({sdpMLineIndex:f.candidate.sdpMLineIndex,candidate:f.candidate.candidate}))});this.peerConnection.addEventListener("track",f=>{this.rawStream=f.streams[0];const g=new Audio;g.autoplay=!0;g.muted=!0;g.onended=function(){g.remove()};g.srcObject=
e.rawStream;e.client.peerTrackHandler(e.peerId,e.rawStream)});this.peerConnection.addStream(this.client.localMediaStream.stream);f&&this.peerConnection.createOffer(g=>{e.peerConnection.setLocalDescription(g,()=>{e.client.descriptionHandler(e.peerId,JSON.stringify(g));1!=e.client.peerStateInitial&&(e.client.peerStateInitial=1)},h=>{console.error('Failed to set local description for "'+e.peerId+'"! '+h);2==e.client.peerStateInitial&&(e.client.peerStateInitial=0);e.client.signalDisconnect(e.peerId)})}, this.rawStream;this.client.peerTrackHandler(this.peerId,this.rawStream)});this.peerConnection.addStream(this.client.localMediaStream.stream);e&&this.peerConnection.createOffer(f=>{this.peerConnection.setLocalDescription(f,()=>{this.client.descriptionHandler(this.peerId,JSON.stringify(f))},g=>{console.error('Failed to set local description for "'+this.peerId+'"! '+g);this.client.signalDisconnect(this.peerId)})},f=>{console.error('Failed to set create offer for "'+this.peerId+'"! '+f);this.client.signalDisconnect(this.peerId)});
g=>{console.error('Failed to set create offer for "'+e.peerId+'"! '+g);2==e.client.peerStateInitial&&(e.client.peerStateInitial=0);e.client.signalDisconnect(e.peerId)});this.peerConnection.addEventListener("connectionstatechange",g=>{"disconnected"===e.peerConnection.connectionState?e.client.signalDisconnect(e.peerId):"connected"===e.peerConnection.connectionState?1!=e.client.peerState&&(e.client.peerState=1):"failed"===e.peerConnection.connectionState&&(2==e.client.peerState&&(e.client.peerState= this.peerConnection.addEventListener("connectionstatechange",f=>{"disconnected"!==this.peerConnection.connectionState&&"failed"!==this.peerConnection.connectionState||this.client.signalDisconnect(this.peerId)})}disconnect(){this.peerConnection.close()}mute(a){this.rawStream.getAudioTracks()[0].enabled=!a}setRemoteDescription(a){try{const b=JSON.parse(a);this.peerConnection.setRemoteDescription(b,()=>{"offer"===b.type&&this.peerConnection.createAnswer(c=>{this.peerConnection.setLocalDescription(c,
0),e.client.signalDisconnect(e.peerId))})}disconnect(){this.peerConnection.close()}mute(a){this.rawStream.getAudioTracks()[0].enabled=!a}setRemoteDescription(a){const b=this;try{const c=JSON.parse(a);this.peerConnection.setRemoteDescription(c,()=>{"offer"==c.type&&b.peerConnection.createAnswer(f=>{b.peerConnection.setLocalDescription(f,()=>{b.client.descriptionHandler(b.peerId,JSON.stringify(f));1!=b.client.peerStateDesc&&(b.client.peerStateDesc=1)},e=>{console.error('Failed to set local description for "'+ ()=>{this.client.descriptionHandler(this.peerId,JSON.stringify(c))},e=>{console.error('Failed to set local description for "'+this.peerId+'"! '+e);this.client.signalDisconnect(this.peerId)})},c=>{console.error('Failed to create answer for "'+this.peerId+'"! '+c);this.client.signalDisconnect(this.peerId)})},c=>{console.error('Failed to set remote description for "'+this.peerId+'"! '+c);this.client.signalDisconnect(this.peerId)})}catch(b){console.error('Failed to parse remote description for "'+this.peerId+
b.peerId+'"! '+e);2==b.client.peerStateDesc&&(b.client.peerStateDesc=0);b.client.signalDisconnect(b.peerId)})},f=>{console.error('Failed to create answer for "'+b.peerId+'"! '+f);2==b.client.peerStateDesc&&(b.client.peerStateDesc=0);b.client.signalDisconnect(b.peerId)})},f=>{console.error('Failed to set remote description for "'+b.peerId+'"! '+f);2==b.client.peerStateDesc&&(b.client.peerStateDesc=0);b.client.signalDisconnect(b.peerId)})}catch(c){console.error('Failed to parse remote description for "'+ '"! '+b),this.client.signalDisconnect(this.peerId)}}addICECandidate(a){try{this.peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(a)))}catch(b){console.error('Failed to parse ice candidate for "'+this.peerId+'"! '+b),this.client.signalDisconnect(this.peerId)}}}class d{constructor(){this.ICEServers=[];this.hasInit=!1;this.peerList=new Map;this.readyState=0;this.microphoneVolumeAudioContext=this.peerDisconnectHandler=this.peerTrackHandler=this.descriptionHandler=this.iceCandidateHandler=
b.peerId+'"! '+c),2==b.client.peerStateDesc&&(b.client.peerStateDesc=0),b.client.signalDisconnect(b.peerId)}}addICECandidate(a){try{this.peerConnection.addIceCandidate(new RTCIceCandidate(JSON.parse(a))),1!=this.client.peerStateIce&&(this.client.peerStateIce=1)}catch(b){console.error('Failed to parse ice candidate for "'+this.peerId+'"! '+b),2==this.client.peerStateIce&&(this.client.peerStateIce=0),this.client.signalDisconnect(this.peerId)}}}class d{constructor(){this.ICEServers=[];this.hasInit=!1; null}voiceClientSupported(){return"undefined"!==typeof window.RTCPeerConnection&&"undefined"!==typeof navigator.mediaDevices&&"undefined"!==typeof navigator.mediaDevices.getUserMedia}setICEServers(a){for(var b=this.ICEServers.length=0;b<a.length;++b){var c=a[b].split(";");1===c.length?this.ICEServers.push({urls:c[0]}):3===c.length&&this.ICEServers.push({urls:c[0],username:c[1],credential:c[2]})}}setICECandidateHandler(a){this.iceCandidateHandler=a}setDescriptionHandler(a){this.descriptionHandler=
this.peerList=new Map;this.readyState=0;this.peerStateIce=this.peerStateDesc=this.peerStateInitial=this.peerStateConnect=this.peerState=2;this.microphoneVolumeAudioContext=this.peerDisconnectHandler=this.peerTrackHandler=this.descriptionHandler=this.iceCandidateHandler=null}voiceClientSupported(){return"undefined"!==typeof window.RTCPeerConnection&&"undefined"!==typeof navigator.mediaDevices&&"undefined"!==typeof navigator.mediaDevices.getUserMedia}setICEServers(a){for(var b=this.ICEServers.length= a}setPeerTrackHandler(a){this.peerTrackHandler=a}setPeerDisconnectHandler(a){this.peerDisconnectHandler=a}activateVoice(a){this.hasInit&&(this.localRawMediaStream.getAudioTracks()[0].enabled=a)}initializeDevices(){this.hasInit?this.readyState=1:navigator.mediaDevices.getUserMedia({audio:!0,video:!1}).then(a=>{this.microphoneVolumeAudioContext=new AudioContext;this.localRawMediaStream=a;this.localRawMediaStream.getAudioTracks()[0].enabled=!1;this.localMediaStream=this.microphoneVolumeAudioContext.createMediaStreamDestination();
0;b<a.length;++b){var c=a[b].split(";");1==c.length?this.ICEServers.push({urls:c[0]}):3==c.length&&this.ICEServers.push({urls:c[0],username:c[1],credential:c[2]})}}setICECandidateHandler(a){this.iceCandidateHandler=a}setDescriptionHandler(a){this.descriptionHandler=a}setPeerTrackHandler(a){this.peerTrackHandler=a}setPeerDisconnectHandler(a){this.peerDisconnectHandler=a}activateVoice(a){this.hasInit&&(this.localRawMediaStream.getAudioTracks()[0].enabled=a)}initializeDevices(){if(this.hasInit)this.readyState= this.localMediaStreamGain=this.microphoneVolumeAudioContext.createGain();this.microphoneVolumeAudioContext.createMediaStreamSource(a).connect(this.localMediaStreamGain);this.localMediaStreamGain.connect(this.localMediaStream);this.readyState=this.localMediaStreamGain.gain.value=1;this.hasInit=!0}).catch(a=>{this.readyState=-1})}setMicVolume(a){this.hasInit&&(.5<a&&(a=.5+2*(a-.5)),1.5<a&&(a=1.5),0>a&&(a=0),this.localMediaStreamGain.gain.value=2*a)}getReadyState(){return this.readyState}signalConnect(a,
1;else{const a=this;navigator.mediaDevices.getUserMedia({audio:!0,video:!1}).then(b=>{a.microphoneVolumeAudioContext=new AudioContext;a.localRawMediaStream=b;a.localRawMediaStream.getAudioTracks()[0].enabled=!1;a.localMediaStream=a.microphoneVolumeAudioContext.createMediaStreamDestination();a.localMediaStreamGain=a.microphoneVolumeAudioContext.createGain();a.microphoneVolumeAudioContext.createMediaStreamSource(b).connect(a.localMediaStreamGain);a.localMediaStreamGain.connect(a.localMediaStream);a.localMediaStreamGain.gain.value= b){try{const c=new RTCPeerConnection({iceServers:this.ICEServers,optional:[{DtlsSrtpKeyAgreement:!0}]}),e=new k(this,a,c,b);this.peerList.set(a,e)}catch(c){}}signalDescription(a,b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==a&&a.setRemoteDescription(b)}signalDisconnect(a,b){var c=this.peerList.get(a);if("undefined"!==typeof c&&null!==c){this.peerList.delete(c);try{c.disconnect()}catch(e){}this.peerDisconnectHandler(a,b)}}mutePeer(a,b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==
1;a.readyState=1;this.hasInit=!0}).catch(b=>{a.readyState=-1})}}setMicVolume(a){this.hasInit&&(.5<a&&(a=.5+2*(a-.5)),1.5<a&&(a=1.5),0>a&&(a=0),this.localMediaStreamGain.gain.value=2*a)}resetPeerStates(){this.peerState=this.peerStateConnect=this.peerStateInitial=this.peerStateDesc=this.peerStateIce=2}getPeerState(){return this.peerState}getPeerStateConnect(){return this.peerStateConnect}getPeerStateInitial(){return this.peerStateInitial}getPeerStateDesc(){return this.peerStateDesc}getPeerStateIce(){return this.peerStateIce}getReadyState(){return this.readyState}signalConnect(a, a&&a.mute(b)}signalICECandidate(a,b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==a&&a.addICECandidate(b)}}window.constructVoiceClient=()=>new d};window.startVoiceClient=()=>{"function"!==typeof window.constructVoiceClient&&window.initializeVoiceClient();return window.constructVoiceClient()};
b){this.hasInit||this.initializeDevices();try{const c=new RTCPeerConnection({iceServers:this.ICEServers,optional:[{DtlsSrtpKeyAgreement:!0}]}),f=new k(this,a,c,b);this.peerList.set(a,f);1!=this.peerStateConnect&&(this.peerStateConnect=1)}catch(c){2==this.peerStateConnect&&(this.peerStateConnect=0)}}signalDescription(a,b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==a&&a.setRemoteDescription(b)}signalDisconnect(a,b){var c=this.peerList.get(a);if("undefined"!==typeof c&&null!==c){this.peerList.delete(c); window.initializeLANClient=()=>{class k{constructor(){this.ICEServers=[];this.dataChannel=this.peerConnection=null;this.readyState=1;this.remotePacketHandler=this.remoteDisconnectHandler=this.remoteDataChannelHandler=this.descriptionHandler=this.iceCandidateHandler=null}LANClientSupported(){return"undefined"!==typeof window.RTCPeerConnection}initializeClient(){try{null!==this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null),null!==this.peerConnection&&this.peerConnection.close(),this.peerConnection=
try{c.disconnect()}catch(f){}this.peerDisconnectHandler(a,b)}}mutePeer(a,b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==a&&a.mute(b)}signalICECandidate(a,b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==a&&a.addICECandidate(b)}}window.constructVoiceClient=()=>new d};window.startVoiceClient=()=>{"function"!==typeof window.constructVoiceClient&&window.initializeVoiceClient();return window.constructVoiceClient()}; new RTCPeerConnection({iceServers:this.ICEServers,optional:[{DtlsSrtpKeyAgreement:!0}]}),this.readyState=1}catch(d){this.readyState=-2}}setICEServers(d){for(var a=this.ICEServers.length=0;a<d.length;++a){var b=d[a].split(";");1===b.length?this.ICEServers.push({urls:b[0]}):3===b.length&&this.ICEServers.push({urls:b[0],username:b[1],credential:b[2]})}}setICECandidateHandler(d){this.iceCandidateHandler=d}setDescriptionHandler(d){this.descriptionHandler=d}setRemoteDataChannelHandler(d){this.remoteDataChannelHandler=
window.initializeLANClient=()=>{class k{constructor(){this.ICEServers=[];this.dataChannel=this.peerConnection=null;this.readyState=1;this.remotePacketHandler=this.remoteDisconnectHandler=this.remoteDataChannelHandler=this.descriptionHandler=this.iceCandidateHandler=null}LANClientSupported(){return"undefined"!==typeof window.RTCPeerConnection}initializeClient(){try{null!=this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null),null!=this.peerConnection&&this.peerConnection.close(),this.peerConnection= d}setRemoteDisconnectHandler(d){this.remoteDisconnectHandler=d}setRemotePacketHandler(d){this.remotePacketHandler=d}getReadyState(){return this.readyState}sendPacketToServer(d){null!==this.dataChannel&&"open"===this.dataChannel.readyState?this.dataChannel.send(d):this.signalRemoteDisconnect(!1)}signalRemoteConnect(){const d=[];this.peerConnection.addEventListener("icecandidate",a=>{if(a.candidate){if(0===d.length){let b=[0,0],c;setTimeout(c=()=>{if(null!==this.peerConnection&&"disconnected"!==this.peerConnection.connectionState){const e=
new RTCPeerConnection({iceServers:this.ICEServers,optional:[{DtlsSrtpKeyAgreement:!0}]}),this.readyState=1}catch(d){this.readyState=-2}}setICEServers(d){for(var a=this.ICEServers.length=0;a<d.length;++a){var b=d[a].split(";");1==b.length?this.ICEServers.push({urls:b[0]}):3==b.length&&this.ICEServers.push({urls:b[0],username:b[1],credential:b[2]})}}setICECandidateHandler(d){this.iceCandidateHandler=d}setDescriptionHandler(d){this.descriptionHandler=d}setRemoteDataChannelHandler(d){this.remoteDataChannelHandler= ++b[1];b[0]!==d.length&&3>e?(b[0]=d.length,setTimeout(c,2E3)):(this.iceCandidateHandler(JSON.stringify(d)),d.length=0)}},2E3)}d.push({sdpMLineIndex:a.candidate.sdpMLineIndex,candidate:a.candidate.candidate})}});this.dataChannel=this.peerConnection.createDataChannel("lan");this.dataChannel.binaryType="arraybuffer";this.dataChannel.addEventListener("open",async a=>{for(;0<d.length;)await new Promise(b=>setTimeout(b,10));this.remoteDataChannelHandler(this.dataChannel)});this.dataChannel.addEventListener("message",
d}setRemoteDisconnectHandler(d){this.remoteDisconnectHandler=d}setRemotePacketHandler(d){this.remotePacketHandler=d}getReadyState(){return this.readyState}sendPacketToServer(d){null!=this.dataChannel&&"open"==this.dataChannel.readyState?this.dataChannel.send(d):this.signalRemoteDisconnect(!1)}signalRemoteConnect(){const d=this,a=[];this.peerConnection.addEventListener("icecandidate",b=>{b.candidate&&(0==a.length&&setTimeout(()=>{null!=d.peerConnection&&"disconnected"!=d.peerConnection.connectionState&& a=>{this.remotePacketHandler(a.data)},!1);this.peerConnection.createOffer(a=>{this.peerConnection.setLocalDescription(a,()=>{this.descriptionHandler(JSON.stringify(a))},b=>{console.error("Failed to set local description! "+b);this.readyState=-1;this.signalRemoteDisconnect(!1)})},a=>{console.error("Failed to set create offer! "+a);this.readyState=-1;this.signalRemoteDisconnect(!1)});this.peerConnection.addEventListener("connectionstatechange",a=>{"disconnected"===this.peerConnection.connectionState?
(d.iceCandidateHandler(JSON.stringify(a)),a.length=0)},3E3),a.push({sdpMLineIndex:b.candidate.sdpMLineIndex,candidate:b.candidate.candidate}))});this.dataChannel=this.peerConnection.createDataChannel("lan");this.dataChannel.binaryType="arraybuffer";this.dataChannel.addEventListener("open",async b=>{for(;0<a.length;)await new Promise(c=>setTimeout(c,0));d.remoteDataChannelHandler(d.dataChannel)});this.dataChannel.addEventListener("message",b=>{d.remotePacketHandler(b.data)},!1);this.peerConnection.createOffer(b=> this.signalRemoteDisconnect(!1):"connected"===this.peerConnection.connectionState?this.readyState=2:"failed"===this.peerConnection.connectionState&&(this.readyState=-1,this.signalRemoteDisconnect(!1))})}signalRemoteDescription(d){try{this.peerConnection.setRemoteDescription(JSON.parse(d))}catch(a){console.error(a),this.readyState=-1,this.signalRemoteDisconnect(!1)}}signalRemoteICECandidate(d){try{const a=JSON.parse(d);for(let b of a)this.peerConnection.addIceCandidate(b)}catch(a){console.error(a),
{d.peerConnection.setLocalDescription(b,()=>{d.descriptionHandler(JSON.stringify(b))},c=>{console.error("Failed to set local description! "+c);d.readyState=-1;d.signalRemoteDisconnect(!1)})},b=>{console.error("Failed to set create offer! "+b);d.readyState=-1;d.signalRemoteDisconnect(!1)});this.peerConnection.addEventListener("connectionstatechange",b=>{"disconnected"===d.peerConnection.connectionState?d.signalRemoteDisconnect(!1):"connected"===d.peerConnection.connectionState?d.readyState=2:"failed"=== this.readyState=-1,this.signalRemoteDisconnect(!1)}}signalRemoteDisconnect(d){null!==this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null);null!==this.peerConnection&&this.peerConnection.close();d||this.remoteDisconnectHandler();this.readyState=0}}window.constructLANClient=()=>new k};window.startLANClient=()=>{"function"!==typeof window.constructLANClient&&window.initializeLANClient();return window.constructLANClient()};
d.peerConnection.connectionState&&(d.readyState=-1,d.signalRemoteDisconnect(!1))})}signalRemoteDescription(d){try{this.peerConnection.setRemoteDescription(JSON.parse(d))}catch(a){console.error(a),this.readyState=-1,this.signalRemoteDisconnect(!1)}}signalRemoteICECandidate(d){try{const a=JSON.parse(d);for(let b of a)this.peerConnection.addIceCandidate(b)}catch(a){console.error(a),this.readyState=-1,this.signalRemoteDisconnect(!1)}}signalRemoteDisconnect(d){null!=this.dataChannel&&(this.dataChannel.close(), window.initializeLANServer=()=>{class k{constructor(a,b,c){this.client=a;this.peerId=b;this.peerConnection=c;this.dataChannel=null;const e=[];let f=!1;this.peerConnection.addEventListener("icecandidate",g=>{if(g.candidate){if(0===e.length){let h=[0,0],l;setTimeout(l=()=>{if(null!==this.peerConnection&&"disconnected"!==this.peerConnection.connectionState){const m=++h[1];h[0]!==e.length&&3>m?(h[0]=e.length,setTimeout(l,2E3)):(this.client.iceCandidateHandler(this.peerId,JSON.stringify(e)),e.length=0,
this.dataChannel=null);null!=this.peerConnection&&this.peerConnection.close();d||this.remoteDisconnectHandler();this.readyState=0}}window.constructLANClient=()=>new k};window.startLANClient=()=>{"function"!==typeof window.constructLANClient&&window.initializeLANClient();return window.constructLANClient()}; f=!0)}},2E3)}e.push({sdpMLineIndex:g.candidate.sdpMLineIndex,candidate:g.candidate.candidate})}});this.peerConnection.addEventListener("datachannel",async g=>{for(;!f;)await new Promise(h=>setTimeout(h,10));this.dataChannel=g.channel;this.client.remoteClientDataChannelHandler(this.peerId,this.dataChannel);this.dataChannel.addEventListener("message",h=>{this.client.remoteClientPacketHandler(this.peerId,h.data)},!1)},!1);this.peerConnection.addEventListener("connectionstatechange",g=>{"disconnected"!==
window.initializeLANServer=()=>{class k{constructor(a,b,c){this.client=a;this.peerId=b;this.peerConnection=c;this.dataChannel=null;const f=this,e=[];this.peerConnection.addEventListener("icecandidate",g=>{g.candidate&&(0==e.length&&setTimeout(()=>{null!=f.peerConnection&&"disconnected"!=f.peerConnection.connectionState&&(f.client.iceCandidateHandler(f.peerId,JSON.stringify(e)),e.length=0)},3E3),e.push({sdpMLineIndex:g.candidate.sdpMLineIndex,candidate:g.candidate.candidate}))});this.peerConnection.addEventListener("datachannel", this.peerConnection.connectionState&&"failed"!==this.peerConnection.connectionState||this.client.signalRemoteDisconnect(this.peerId)})}disconnect(){null!==this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null);this.peerConnection.close()}setRemoteDescription(a){try{const b=JSON.parse(a);this.peerConnection.setRemoteDescription(b,()=>{"offer"===b.type&&this.peerConnection.createAnswer(c=>{this.peerConnection.setLocalDescription(c,()=>{this.client.descriptionHandler(this.peerId,JSON.stringify(c))},
async g=>{for(;0<e.length;)await new Promise(h=>setTimeout(h,0));f.dataChannel=g.channel;f.client.remoteClientDataChannelHandler(f.peerId,f.dataChannel);f.dataChannel.addEventListener("message",h=>{f.client.remoteClientPacketHandler(f.peerId,h.data)},!1)},!1);this.peerConnection.addEventListener("connectionstatechange",g=>{"disconnected"===f.peerConnection.connectionState?f.client.signalRemoteDisconnect(f.peerId):"connected"===f.peerConnection.connectionState?1!=f.client.peerState&&(f.client.peerState= e=>{console.error('Failed to set local description for "'+this.peerId+'"! '+e);this.client.signalRemoteDisconnect(this.peerId)})},c=>{console.error('Failed to create answer for "'+this.peerId+'"! '+c);this.client.signalRemoteDisconnect(this.peerId)})},c=>{console.error('Failed to set remote description for "'+this.peerId+'"! '+c);this.client.signalRemoteDisconnect(this.peerId)})}catch(b){console.error('Failed to parse remote description for "'+this.peerId+'"! '+b),this.client.signalRemoteDisconnect(this.peerId)}}addICECandidate(a){try{const b=
1):"failed"===f.peerConnection.connectionState&&(2==f.client.peerState&&(f.client.peerState=0),f.client.signalRemoteDisconnect(f.peerId))})}disconnect(){null!=this.dataChannel&&(this.dataChannel.close(),this.dataChannel=null);this.peerConnection.close()}setRemoteDescription(a){const b=this;try{const c=JSON.parse(a);this.peerConnection.setRemoteDescription(c,()=>{"offer"==c.type&&b.peerConnection.createAnswer(f=>{b.peerConnection.setLocalDescription(f,()=>{b.client.descriptionHandler(b.peerId,JSON.stringify(f)); JSON.parse(a);for(let c of b)this.peerConnection.addIceCandidate(new RTCIceCandidate(c))}catch(b){console.error('Failed to parse ice candidate for "'+this.peerId+'"! '+b),this.client.signalRemoteDisconnect(this.peerId)}}}class d{constructor(){this.ICEServers=[];this.hasInit=!1;this.peerList=new Map;this.remoteClientPacketHandler=this.remoteClientDisconnectHandler=this.remoteClientDataChannelHandler=this.descriptionHandler=this.iceCandidateHandler=null}LANServerSupported(){return"undefined"!==typeof window.RTCPeerConnection}initializeServer(){}setICEServers(a){for(var b=
1!=b.client.peerStateDesc&&(b.client.peerStateDesc=1)},e=>{console.error('Failed to set local description for "'+b.peerId+'"! '+e);2==b.client.peerStateDesc&&(b.client.peerStateDesc=0);b.client.signalRemoteDisconnect(b.peerId)})},f=>{console.error('Failed to create answer for "'+b.peerId+'"! '+f);2==b.client.peerStateDesc&&(b.client.peerStateDesc=0);b.client.signalRemoteDisconnect(b.peerId)})},f=>{console.error('Failed to set remote description for "'+b.peerId+'"! '+f);2==b.client.peerStateDesc&& this.ICEServers.length=0;b<a.length;++b){var c=a[b].split(";");1===c.length?this.ICEServers.push({urls:c[0]}):3===c.length&&this.ICEServers.push({urls:c[0],username:c[1],credential:c[2]})}}setICECandidateHandler(a){this.iceCandidateHandler=a}setDescriptionHandler(a){this.descriptionHandler=a}setRemoteClientDataChannelHandler(a){this.remoteClientDataChannelHandler=a}setRemoteClientDisconnectHandler(a){this.remoteClientDisconnectHandler=a}setRemoteClientPacketHandler(a){this.remoteClientPacketHandler=
(b.client.peerStateDesc=0);b.client.signalRemoteDisconnect(b.peerId)})}catch(c){console.error('Failed to parse remote description for "'+b.peerId+'"! '+c),2==b.client.peerStateDesc&&(b.client.peerStateDesc=0),b.client.signalRemoteDisconnect(b.peerId)}}addICECandidate(a){try{const b=JSON.parse(a);for(let c of b)this.peerConnection.addIceCandidate(new RTCIceCandidate(c));1!=this.client.peerStateIce&&(this.client.peerStateIce=1)}catch(b){console.error('Failed to parse ice candidate for "'+this.peerId+ a}sendPacketToRemoteClient(a,b){var c=this.peerList.get(a);"undefined"!==typeof c&&null!==c&&(null!=c.dataChannel&&"open"===c.dataChannel.readyState?c.dataChannel.send(b):this.signalRemoteDisconnect(a))}signalRemoteConnect(a){try{const b=new RTCPeerConnection({iceServers:this.ICEServers,optional:[{DtlsSrtpKeyAgreement:!0}]}),c=new k(this,a,b);this.peerList.set(a,c)}catch(b){}}signalRemoteDescription(a,b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==a&&a.setRemoteDescription(b)}signalRemoteICECandidate(a,
'"! '+b),2==this.client.peerStateIce&&(this.client.peerStateIce=0),this.client.signalRemoteDisconnect(this.peerId)}}}class d{constructor(){this.ICEServers=[];this.hasInit=!1;this.peerList=new Map;this.peerStateIce=this.peerStateDesc=this.peerStateInitial=this.peerStateConnect=this.peerState=2;this.remoteClientPacketHandler=this.remoteClientDisconnectHandler=this.remoteClientDataChannelHandler=this.descriptionHandler=this.iceCandidateHandler=null}LANServerSupported(){return"undefined"!==typeof window.RTCPeerConnection}initializeServer(){}setICEServers(a){for(var b= b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==a&&a.addICECandidate(b)}signalRemoteDisconnect(a){if(0===a.length){for(var b of this.peerList.values())if("undefined"!==typeof b&&null!==b){this.peerList.delete(a);try{b.disconnect()}catch(c){}this.remoteClientDisconnectHandler(a)}this.peerList.clear()}else if(b=this.peerList.get(a),"undefined"!==typeof b&&null!==b){this.peerList.delete(a);try{b.disconnect()}catch(c){}this.remoteClientDisconnectHandler(a)}}countPeers(){return this.peerList.size}}
this.ICEServers.length=0;b<a.length;++b){var c=a[b].split(";");1==c.length?this.ICEServers.push({urls:c[0]}):3==c.length&&this.ICEServers.push({urls:c[0],username:c[1],credential:c[2]})}}setICECandidateHandler(a){this.iceCandidateHandler=a}setDescriptionHandler(a){this.descriptionHandler=a}setRemoteClientDataChannelHandler(a){this.remoteClientDataChannelHandler=a}setRemoteClientDisconnectHandler(a){this.remoteClientDisconnectHandler=a}setRemoteClientPacketHandler(a){this.remoteClientPacketHandler= window.constructLANServer=()=>new d};window.startLANServer=()=>{"function"!==typeof window.constructLANServer&&window.initializeLANServer();return window.constructLANServer()};
a}sendPacketToRemoteClient(a,b){var c=this.peerList.get(a);"undefined"!==typeof c&&null!==c&&(null!=c.dataChannel&&"open"==c.dataChannel.readyState?c.dataChannel.send(b):this.signalRemoteDisconnect(a))}resetPeerStates(){this.peerState=this.peerStateConnect=this.peerStateInitial=this.peerStateDesc=this.peerStateIce=2}getPeerState(){return this.peerState}getPeerStateConnect(){return this.peerStateConnect}getPeerStateInitial(){return this.peerStateInitial}getPeerStateDesc(){return this.peerStateDesc}getPeerStateIce(){return this.peerStateIce}signalRemoteConnect(a){try{const b=
new RTCPeerConnection({iceServers:this.ICEServers,optional:[{DtlsSrtpKeyAgreement:!0}]}),c=new k(this,a,b);this.peerList.set(a,c);1!=this.peerStateConnect&&(this.peerStateConnect=1)}catch(b){2==this.peerStateConnect&&(this.peerStateConnect=0)}}signalRemoteDescription(a,b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==a&&a.setRemoteDescription(b)}signalRemoteICECandidate(a,b){a=this.peerList.get(a);"undefined"!==typeof a&&null!==a&&a.addICECandidate(b)}signalRemoteDisconnect(a){if(0==a.length){for(var b of this.peerList.values())if("undefined"!==
typeof b&&null!==b){this.peerList.delete(a);try{b.disconnect()}catch(c){}this.remoteClientDisconnectHandler(a)}this.peerList.clear()}else if(b=this.peerList.get(a),"undefined"!==typeof b&&null!==b){this.peerList.delete(a);try{b.disconnect()}catch(c){}this.remoteClientDisconnectHandler(a)}}countPeers(){return this.peerList.size}}window.constructLANServer=()=>new d};window.startLANServer=()=>{"function"!==typeof window.constructLANServer&&window.initializeLANServer();return window.constructLANServer()};