Add Room logics
48
main.go
|
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"html/template"
|
||||
"math/rand"
|
||||
"os"
|
||||
"strconv"
|
||||
|
|
@ -18,7 +19,6 @@ import (
|
|||
"github.com/giongto35/cloud-game/util"
|
||||
"github.com/giongto35/cloud-game/webrtc"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
"encoding/json"
|
||||
|
|
@ -27,7 +27,12 @@ import (
|
|||
// var webRTC *webrtc.WebRTC
|
||||
var width = 256
|
||||
var height = 240
|
||||
var index string = "./index_http.html"
|
||||
var indexFN string = "./static/gameboy.html"
|
||||
var service string = "http"
|
||||
|
||||
type IndexPageData struct {
|
||||
Service string
|
||||
}
|
||||
|
||||
var upgrader = websocket.Upgrader{}
|
||||
|
||||
|
|
@ -37,6 +42,12 @@ type WSPacket struct {
|
|||
RoomID string `json:"room_id"`
|
||||
}
|
||||
|
||||
type SessionPacket struct {
|
||||
Game string `json:"game"`
|
||||
RoomID string `json:"room_id"`
|
||||
SDP string `json:"sdp"`
|
||||
}
|
||||
|
||||
type Room struct {
|
||||
imageChannel chan *image.RGBA
|
||||
inputChannel chan int
|
||||
|
|
@ -57,30 +68,35 @@ func main() {
|
|||
fmt.Printf("Usage: %s [ws]\n", os.Args[0])
|
||||
fmt.Println("http://localhost:8000")
|
||||
rooms = map[string]*Room{}
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
service = "ws"
|
||||
log.Println("Using websocket")
|
||||
index = "./index_ws.html"
|
||||
|
||||
// ignore origin
|
||||
upgrader.CheckOrigin = func(r *http.Request) bool { return true }
|
||||
http.HandleFunc("/", getWeb)
|
||||
http.HandleFunc("/ws", ws)
|
||||
http.ListenAndServe(":8000", nil)
|
||||
} else {
|
||||
log.Println("Using http")
|
||||
router := mux.NewRouter()
|
||||
router.HandleFunc("/", getWeb).Methods("GET")
|
||||
router.HandleFunc("/session", postSession).Methods("POST")
|
||||
http.ListenAndServe(":8000", router)
|
||||
http.HandleFunc("/session", postSession)
|
||||
}
|
||||
|
||||
http.HandleFunc("/", getWeb)
|
||||
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
|
||||
http.ListenAndServe(":8000", nil)
|
||||
}
|
||||
|
||||
func getWeb(w http.ResponseWriter, r *http.Request) {
|
||||
bs, err := ioutil.ReadFile(index)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
tmpl := template.Must(template.ParseFiles(indexFN))
|
||||
data := IndexPageData{
|
||||
Service: service,
|
||||
}
|
||||
w.Write(bs)
|
||||
tmpl.Execute(w, data)
|
||||
// bs, err := ioutil.ReadFile(index)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// w.Write(bs)
|
||||
}
|
||||
|
||||
// initRoom initilize room returns roomID
|
||||
|
|
@ -195,12 +211,6 @@ func ws(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
}
|
||||
|
||||
type SessionPacket struct {
|
||||
Game string `json:"game"`
|
||||
RoomID string `json:"room_id"`
|
||||
SDP string `json:"sdp"`
|
||||
}
|
||||
|
||||
func postSession(w http.ResponseWriter, r *http.Request) {
|
||||
bs, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
|
|
|
|||
6922
static/css/gameboy.css
Normal file
BIN
static/fonts/8-Bit-Madness.ttf
Normal file
161
static/gameboy.html
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
<head>
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
|
||||
|
||||
<link type="text/css" rel="stylesheet" href="/static/css/gameboy.css">
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="gameboy" class="green">
|
||||
<div id="canvas"></div>
|
||||
<div id="border"></div>
|
||||
<div id="border-top"></div>
|
||||
<div id="border-left"></div>
|
||||
<div id="border-bottom"></div>
|
||||
<div id="border-right"></div>
|
||||
<div id="screw-small-right" class="screw small"></div>
|
||||
<div id="screw-small-left" class="screw small"></div>
|
||||
<div id="screw-large-right" class="screw large"></div>
|
||||
<div id="screw-large-left" class="screw large"></div>
|
||||
<div id="backboard"></div>
|
||||
<div id="motherboard"></div>
|
||||
<div id="motherboard-capacitors" class="capacitors"></div>
|
||||
<div id="chip-short" class="chip"></div>
|
||||
<div id="chip-diagonal" class="chip"></div>
|
||||
<div id="chip-tall" class="chip"></div>
|
||||
<div id="chip-capacitors" class="capacitors"></div>
|
||||
<div id="contrast-knob"></div>
|
||||
<div id="link-port"></div>
|
||||
<div id="circuit-bottom" class="circuit"></div>
|
||||
<div id="circuit-top" class="circuit"></div>
|
||||
<div id="transistors"></div>
|
||||
<div id="processor"></div>
|
||||
<div id="component"></div>
|
||||
<div id="controller"></div>
|
||||
<div id="speaker"></div>
|
||||
<div id="whitescreen"></div>
|
||||
<div id="glass"></div>
|
||||
<div id="glass-gameboy-text">GAME BOY</div>
|
||||
<div id="glass-color-text">C</div>
|
||||
<div id="screen">
|
||||
<video id="loading-screen" autoplay=true poster="https://orig00.deviantart.net/cdcd/f/2017/276/a/a/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif">
|
||||
</video>
|
||||
|
||||
<div id="menu-screen">
|
||||
<img id="box-art" src="/static/img/boxarts/Contra (USA).png">
|
||||
<div id="title">
|
||||
<p>Contra</p>
|
||||
</div>
|
||||
|
||||
<img class="arrow left" src="/static/img/arrow2_e.gif">
|
||||
<img class="arrow right" src="/static/img/arrow2_e.gif">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="screen-gameboy-text">GAME BOY</div>
|
||||
<div id="screen-nintendo-text">Nintendo</div>
|
||||
<div id="joystick-pad"></div>
|
||||
<div id="joystick">.</div>
|
||||
<div id="control"></div>
|
||||
<div id="control-b" class="control-button">B</div>
|
||||
<div id="control-a" class="control-button">A</div>
|
||||
<div id="start-select-box"></div>
|
||||
<div id="start-select-button"></div>
|
||||
<div id="cover-vertical"></div>
|
||||
<div id="cover-horizontal"></div>
|
||||
<div id="gloss"></div>
|
||||
<div id="speaker-holes"></div>
|
||||
<div id="power"></div>
|
||||
</div>
|
||||
|
||||
<div id="colors">
|
||||
<div class="color" data-color="red"></div>
|
||||
<div class="color" data-color="purple"></div>
|
||||
<div class="color" data-color="green"></div>
|
||||
<div class="color" data-color="yellow"></div>
|
||||
<div class="color" data-color="teal"></div>
|
||||
<div class="color" data-color="transparent"></div><br><br>
|
||||
<div>Animation powered by bchanx <a href="http://bchanx.com/animated-gameboy-in-css-blog"><i class="fas fa-blog"></i></a> <a href="https://github.com/bchanx/animated-gameboy-in-css"><i class="fab fa-github"></i></a></div><br>
|
||||
<div>Rocked by <a href="https://github.com/giongto35">giongto35</a> <i class="far fa-handshake"></i> <a href="https://trich.im">trichimtrich</a></div>
|
||||
</div>
|
||||
|
||||
<div style="position:absolute; top:30%; left: 5%;">
|
||||
<h3>Instruction</h3>
|
||||
<div>
|
||||
Use Up, Down, Left, Right to Move <br />
|
||||
Z to jump (A) <br />
|
||||
X to sprint (B) <br />
|
||||
C is start (in game) <br />
|
||||
V is select game <br />
|
||||
Q is super quit <br />
|
||||
|
||||
Room ID: <input type="text" id="roomID"><br>
|
||||
<!--Fullscreen media for better gaming experience<br /-->
|
||||
</div><br>
|
||||
|
||||
<h3>Log:</h3>
|
||||
<pre id="div"></pre>
|
||||
|
||||
<div>
|
||||
<i class="fas fa-gamepad"></i> <u><i>Refresh to retry when checking is too long</i></u>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||
<script src="/static/js/const.js"></script>
|
||||
|
||||
<script>
|
||||
/*
|
||||
GLOBAL VARS
|
||||
*/
|
||||
// Game state
|
||||
screenState = "loader";
|
||||
gameIdx = 0;
|
||||
|
||||
// Input vars
|
||||
keyState = {
|
||||
// controllers
|
||||
a: false,
|
||||
b: false,
|
||||
start: false,
|
||||
select: false,
|
||||
|
||||
// navigators
|
||||
up: false,
|
||||
down: false,
|
||||
left: false,
|
||||
right: false,
|
||||
|
||||
// unofficial
|
||||
quit: false
|
||||
}
|
||||
|
||||
stateUnchange = true;
|
||||
unchangePacket = INPUT_STATE_PACKET;
|
||||
inputTimer = null;
|
||||
|
||||
// Connection vars
|
||||
var pc, inputChannel;
|
||||
var localSessionDescription = "";
|
||||
var remoteSessionDescription = "";
|
||||
var conn;
|
||||
|
||||
|
||||
/*
|
||||
FUNCTIONS
|
||||
*/
|
||||
|
||||
// miscs
|
||||
function log(msg) {
|
||||
if (DEBUG) {
|
||||
document.getElementById('div').innerHTML += msg + '<br>'
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script src="/static/js/gameboy_loader.js"></script>
|
||||
<script src="/static/js/gameboy_controller.js"></script>
|
||||
<script src="/static/js/{{.Service}}.js"></script>
|
||||
</body>
|
||||
BIN
static/img/arrow2_e.gif
Normal file
|
After Width: | Height: | Size: 151 B |
BIN
static/img/boxarts/Contra (USA).png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
static/img/boxarts/Kirby's Adventure (Canada).png
Normal file
|
After Width: | Height: | Size: 154 KiB |
BIN
static/img/boxarts/Mega Man 2 (USA).png
Normal file
|
After Width: | Height: | Size: 156 KiB |
BIN
static/img/boxarts/Metal Gear (USA).png
Normal file
|
After Width: | Height: | Size: 188 KiB |
BIN
static/img/boxarts/Super Mario Bros. (World).png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
static/img/boxarts/Super Mario Bros. 2 (USA).png
Normal file
|
After Width: | Height: | Size: 152 KiB |
BIN
static/img/boxarts/Super Mario Bros. 3 (USA).png
Normal file
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 207 KiB |
|
After Width: | Height: | Size: 83 KiB |
|
After Width: | Height: | Size: 338 KiB |
0
static/index.html
Normal file
|
|
@ -109,9 +109,7 @@ window.startGame = () => {
|
|||
// webrtc
|
||||
pc = new RTCPeerConnection({iceServers: [{urls: 'stun:stun.l.google.com:19302'}]})
|
||||
// input channel
|
||||
let r = Math.random().toString(36).substring(7)
|
||||
console.log("data chan name", r)
|
||||
inputChannel = pc.createDataChannel(r)
|
||||
inputChannel = pc.createDataChannel('foo')
|
||||
inputChannel.onclose = () => {
|
||||
log('inputChannel has closed');
|
||||
}
|
||||
|
|
@ -173,6 +171,10 @@ var localSessionDescription = "";
|
|||
var remoteSessionDescription = "";
|
||||
var conn;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Input handler
|
||||
keyState = {
|
||||
// controllers
|
||||
100
static/js/const.js
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
// miscs
|
||||
DEBUG = true;
|
||||
|
||||
// list game
|
||||
GAME_LIST = [
|
||||
{
|
||||
name: "Contra",
|
||||
nes: "Contra.nes",
|
||||
art: "Contra (USA).png"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Kirby's Adventure",
|
||||
nes: "Kirby's Adventure.nes",
|
||||
art: "Kirby's Adventure (Canada).png"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Mega Man 2",
|
||||
nes: "Mega Man 2.nes",
|
||||
art: "Mega Man 2 (USA).png"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Metal Gear",
|
||||
nes: "Metal Gear.nes",
|
||||
art: "Metal Gear (USA).png"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Mortal Kombat 4",
|
||||
nes: "Mortal Kombat 4.nes",
|
||||
art: "mortal-kombat-x-box-art-revealed-as-pre-orders-ope_5pxd.jpg"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Super Mario Bros 2",
|
||||
nes: "Super Mario Bros 2.nes",
|
||||
art: "Super Mario Bros. 2 (USA).png"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Super Mario Bros 3",
|
||||
nes: "Super Mario Bros 3.nes",
|
||||
art: "Super Mario Bros. 3 (USA).png"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Super Mario Bros",
|
||||
nes: "Super Mario Bros.nes",
|
||||
art: "Super Mario Bros. (World).png"
|
||||
},
|
||||
|
||||
{
|
||||
name: "TMNT 3",
|
||||
nes: "Teenage Mutant Ninja Turtles 3.nes",
|
||||
art: "Teenage Mutant Ninja Turtles III - The Manhattan Project (USA).png"
|
||||
},
|
||||
|
||||
{
|
||||
name: "Zelda",
|
||||
nes: "zelda.nes",
|
||||
art: "Zelda II - The Adventure of Link (Europe) (Rev A).png"
|
||||
}
|
||||
];
|
||||
|
||||
|
||||
|
||||
// Keyboard stuffs
|
||||
KEY_MAP = {
|
||||
37: "left",
|
||||
38: "up",
|
||||
39: "right",
|
||||
40: "down",
|
||||
|
||||
90: "a", // z
|
||||
88: "b", // x
|
||||
67: "start", // c
|
||||
86: "select", // v
|
||||
|
||||
81: "quit" // q
|
||||
}
|
||||
|
||||
/*
|
||||
const (
|
||||
ButtonA = iota
|
||||
ButtonB
|
||||
ButtonSelect
|
||||
ButtonStart
|
||||
ButtonUp
|
||||
ButtonDown
|
||||
ButtonLeft
|
||||
ButtonRight
|
||||
)
|
||||
*/
|
||||
KEY_BIT = ["a", "b", "select", "start", "up", "down", "left", "right"];
|
||||
|
||||
|
||||
INPUT_FPS = 100;
|
||||
INPUT_STATE_PACKET = 5;
|
||||
135
static/js/gameboy_controller.js
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
|
||||
// menu screen
|
||||
function showMenuScreen() {
|
||||
log("Clean up connection / frame");
|
||||
// clean up before / after menu
|
||||
try {
|
||||
inputChannel.close();
|
||||
} catch (err) {
|
||||
log(`> [Warning] input channel: ${err}`);
|
||||
}
|
||||
|
||||
try {
|
||||
pc.close();
|
||||
} catch (err) {
|
||||
log(`> [Warning] peer connection: ${err}`);
|
||||
}
|
||||
|
||||
try {
|
||||
conn.close();
|
||||
} catch (err) {
|
||||
log(`> [Warning] Websocket connection: ${err}`);
|
||||
}
|
||||
|
||||
|
||||
$("#loading-screen").hide();
|
||||
$("#menu-screen").hide();
|
||||
|
||||
// show
|
||||
|
||||
$("#loading-screen").show().delay(1000).fadeOut(400, () => {
|
||||
log("Loading menu screen");
|
||||
$("#menu-screen").fadeIn(400, () => {
|
||||
chooseGame(7);
|
||||
screenState = "menu";
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// game menu
|
||||
function chooseGame(idx) {
|
||||
if (idx < 0 || idx == gameIdx || idx >= GAME_LIST.length) return false;
|
||||
|
||||
$("#menu-screen #box-art").fadeOut(400, function () {
|
||||
$(this).attr("src", `/static/img/boxarts/${GAME_LIST[idx].art}`);
|
||||
$(this).fadeIn(400, function () {
|
||||
$("#menu-screen #title p").html(GAME_LIST[idx].name);
|
||||
});
|
||||
});
|
||||
|
||||
if (idx == 0) {
|
||||
$("#menu-screen .left").hide();
|
||||
} else {
|
||||
$("#menu-screen .left").show();
|
||||
}
|
||||
|
||||
if (idx == GAME_LIST.length - 1) {
|
||||
$("#menu-screen .right").hide();
|
||||
} else {
|
||||
$("#menu-screen .right").show();
|
||||
}
|
||||
|
||||
gameIdx = idx;
|
||||
log(`> [Pick] game ${gameIdx + 1}/${GAME_LIST.length} - ${GAME_LIST[gameIdx].name}`);
|
||||
}
|
||||
|
||||
function setState(e, bo) {
|
||||
if (e.keyCode in KEY_MAP) {
|
||||
keyState[KEY_MAP[e.keyCode]] = bo;
|
||||
stateUnchange = false;
|
||||
unchangePacket = INPUT_STATE_PACKET;
|
||||
}
|
||||
}
|
||||
|
||||
document.body.onkeyup = function (e) {
|
||||
if (screenState === "menu") {
|
||||
switch (KEY_MAP[e.keyCode]) {
|
||||
case "left":
|
||||
chooseGame(gameIdx - 1);
|
||||
break;
|
||||
|
||||
case "right":
|
||||
chooseGame(gameIdx + 1);
|
||||
break;
|
||||
|
||||
case "select":
|
||||
startGame();
|
||||
}
|
||||
} else if (screenState === "game") {
|
||||
setState(e, false);
|
||||
}
|
||||
|
||||
// global reset
|
||||
if (KEY_MAP[e.keyCode] == "quit") {
|
||||
endInput();
|
||||
showMenuScreen();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
document.body.onkeydown = function (e) {
|
||||
if (screenState === "game")
|
||||
setState(e, true);
|
||||
};
|
||||
|
||||
|
||||
function sendInput() {
|
||||
// prepare key
|
||||
if (stateUnchange || unchangePacket > 0) {
|
||||
st = "";
|
||||
KEY_BIT.forEach(elem => {
|
||||
st += keyState[elem] ? 1 : 0;
|
||||
});
|
||||
ss = parseInt(st, 2);
|
||||
console.log(`Key state string: ${st} ==> ${ss}`);
|
||||
|
||||
// send
|
||||
inputChannel.send(ss);
|
||||
|
||||
stateUnchange = false;
|
||||
unchangePacket--;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function startInput() {
|
||||
if (inputTimer == null) {
|
||||
inputTimer = setInterval(sendInput, 1000 / INPUT_FPS)
|
||||
}
|
||||
}
|
||||
|
||||
function endInput() {
|
||||
clearInterval(inputTimer);
|
||||
inputTimer = null;
|
||||
}
|
||||
35
static/js/gameboy_loader.js
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
// A tiny bit of javascript code for color selection
|
||||
window.onload = function() {
|
||||
var colors = ['red', 'purple', 'green', 'yellow', 'teal', 'transparent'];
|
||||
var last = null;
|
||||
Array.prototype.slice.call(document.querySelectorAll('.color')).forEach(function(el) {
|
||||
el.addEventListener('click', function() {
|
||||
log("Starting gameboy");
|
||||
screenState = "loader";
|
||||
|
||||
if (last) {
|
||||
last.classList.remove('active');
|
||||
}
|
||||
var color = el.getAttribute('data-color');
|
||||
var gameboy = document.querySelector('#gameboy');
|
||||
gameboy.style.opacity = 0;
|
||||
gameboy.classList.remove(gameboy.classList[0]);
|
||||
var clone = gameboy.cloneNode(true);
|
||||
gameboy.remove();
|
||||
clone.classList.add(color);
|
||||
clone.style.opacity = 1;
|
||||
var colors = document.querySelector('#colors');
|
||||
colors.parentNode.insertBefore(clone, colors);
|
||||
el.classList.add('active');
|
||||
last = el;
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelector('.color[data-color="green"]').dispatchEvent(new MouseEvent('click', {
|
||||
'view': window,
|
||||
'bubbles': true
|
||||
}));
|
||||
|
||||
$("#screen-gameboy-text").on("webkitAnimationEnd", showMenuScreen);
|
||||
$("#screen-gameboy-text").on("animationend", showMenuScreen);
|
||||
}
|
||||
94
static/js/http.js
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
// http signal server
|
||||
|
||||
function startGame() {
|
||||
log("Starting game screen")
|
||||
|
||||
// clear
|
||||
endInput();
|
||||
document.getElementById('div').innerHTML = "";
|
||||
$("#loading-screen").show();
|
||||
$("#menu-screen").fadeOut();
|
||||
// end clear
|
||||
|
||||
|
||||
// Register with server the session description
|
||||
function postSession(session) {
|
||||
if (session == "") {
|
||||
return;
|
||||
}
|
||||
var xhttp = new XMLHttpRequest();
|
||||
xhttp.onreadystatechange = function () {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
remoteSessionDescription = this.responseText;
|
||||
// document.getElementById('playGame').disabled = false;
|
||||
// by original design, we would click to start.
|
||||
startSession();
|
||||
}
|
||||
};
|
||||
xhttp.open("POST", "/session", true);
|
||||
xhttp.setRequestHeader("Content-type", "application/json");
|
||||
xhttp.send(JSON.stringify({ "game": GAME_LIST[gameIdx].nes, "sdp": session }));
|
||||
|
||||
}
|
||||
let pc = new RTCPeerConnection({
|
||||
iceServers: [
|
||||
{
|
||||
urls: 'stun:stun.l.google.com:19302'
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
inputChannel = pc.createDataChannel('foo')
|
||||
inputChannel.onclose = () => log('inputChannel has closed')
|
||||
inputChannel.onopen = () => log('inputChannel has opened')
|
||||
inputChannel.onmessage = e => log(`Message from DataChannel '${inputChannel.label}' payload '${e.data}'`)
|
||||
|
||||
pc.ontrack = function (event) {
|
||||
log("New stream, yay!");
|
||||
document.getElementById("loading-screen").srcObject = event.streams[0];
|
||||
|
||||
// var el = document.createElement(event.track.kind)
|
||||
// el.srcObject = event.streams[0]
|
||||
// el.autoplay = true
|
||||
// el.width = 800;
|
||||
// el.height = 600;
|
||||
// el.poster = new URL("https://orig00.deviantart.net/cdcd/f/2017/276/a/a/october_2nd___gameboy_poltergeist_by_wanyo-dbpdmnd.gif");
|
||||
|
||||
// document.getElementById('remoteVideos').appendChild(el)
|
||||
}
|
||||
|
||||
pc.onicecandidate = event => {
|
||||
if (event.candidate === null) {
|
||||
var session = btoa(JSON.stringify(pc.localDescription));
|
||||
localSessionDescription = session;
|
||||
postSession(session)
|
||||
}
|
||||
}
|
||||
pc.createOffer({ offerToReceiveVideo: true, offerToReceiveAudio: true }).then(d => pc.setLocalDescription(d)).catch(log)
|
||||
|
||||
window.startSession = () => {
|
||||
let sd = remoteSessionDescription
|
||||
if (sd === '') {
|
||||
return alert('Session Description must not be empty')
|
||||
}
|
||||
try {
|
||||
pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(sd))));
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
}
|
||||
|
||||
pc.oniceconnectionstatechange = e => {
|
||||
log(`iceConnectionState: ${pc.iceConnectionState}`);
|
||||
|
||||
if (pc.iceConnectionState === "connected") {
|
||||
startInput();
|
||||
screenState = "game";
|
||||
}
|
||||
else if (pc.iceConnectionState === "disconnected") {
|
||||
endInput();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
114
static/js/ws.js
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
// web socket
|
||||
|
||||
function startGame() {
|
||||
log("Starting game screen");
|
||||
|
||||
// clear
|
||||
endInput();
|
||||
document.getElementById('div').innerHTML = "";
|
||||
$("#loading-screen").show();
|
||||
$("#menu-screen").fadeOut();
|
||||
// end clear
|
||||
|
||||
conn = new WebSocket(`ws://${location.host}/ws`);
|
||||
|
||||
conn.onopen = () => {
|
||||
log("WebSocket is opened. Send ping");
|
||||
roomID = document.getElementById('roomID').value
|
||||
conn.send(JSON.stringify({"id": "ping", "data": GAME_LIST[gameIdx].nes, "room_id": roomID}));
|
||||
}
|
||||
|
||||
conn.onerror = error => {
|
||||
log(`Websocket error: ${error}`);
|
||||
}
|
||||
|
||||
conn.onclose = () => {
|
||||
log("Websocket closed");
|
||||
}
|
||||
|
||||
conn.onmessage = e => {
|
||||
d = JSON.parse(e.data);
|
||||
switch (d["id"]) {
|
||||
case "pong":
|
||||
log("Recv pong. Start webrtc");
|
||||
startWebRTC();
|
||||
break;
|
||||
case "sdp":
|
||||
log("Got remote sdp");
|
||||
pc.setRemoteDescription(new RTCSessionDescription(JSON.parse(atob(d["data"]))));
|
||||
break;
|
||||
case "start":
|
||||
log("Got start");
|
||||
document.getElementById('roomID').value = d["room_id"]
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// webrtc
|
||||
pc = new RTCPeerConnection({iceServers: [{urls: 'stun:stun.l.google.com:19302'}]})
|
||||
// input channel
|
||||
inputChannel = pc.createDataChannel('foo')
|
||||
inputChannel.onclose = () => {
|
||||
log('inputChannel has closed');
|
||||
}
|
||||
|
||||
inputChannel.onopen = () => {
|
||||
log('inputChannel has opened');
|
||||
}
|
||||
|
||||
inputChannel.onmessage = e => {
|
||||
log(`Message from DataChannel '${inputChannel.label}' payload '${e.data}'`);
|
||||
}
|
||||
|
||||
|
||||
pc.oniceconnectionstatechange = e => {
|
||||
log(`iceConnectionState: ${pc.iceConnectionState}`);
|
||||
|
||||
if (pc.iceConnectionState === "connected") {
|
||||
conn.send(JSON.stringify({"id": "start", "data": ""}));
|
||||
startInput();
|
||||
screenState = "game";
|
||||
}
|
||||
else if (pc.iceConnectionState === "disconnected") {
|
||||
endInput();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// stream channel
|
||||
pc.ontrack = function (event) {
|
||||
log("New stream, yay!");
|
||||
document.getElementById("loading-screen").srcObject = event.streams[0];
|
||||
// document.getElementById("loading-screen").width = 270;
|
||||
// document.getElementById("loading-screen").height = 240;
|
||||
}
|
||||
|
||||
|
||||
// candidate packet from STUN
|
||||
pc.onicecandidate = event => {
|
||||
if (event.candidate === null) {
|
||||
|
||||
} else {
|
||||
console.log(JSON.stringify(event.candidate));
|
||||
// conn.send(JSON.stringify({"id": "candidate", "data": JSON.stringify(event.candidate)}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function startWebRTC() {
|
||||
// receiver only tracks
|
||||
pc.addTransceiver('video', {'direction': 'recvonly'});
|
||||
|
||||
// create SDP
|
||||
pc.createOffer({offerToReceiveVideo: true, offerToReceiveAudio: false}).then(d => {
|
||||
pc.setLocalDescription(d, () => {
|
||||
// send to ws
|
||||
session = btoa(JSON.stringify(pc.localDescription));
|
||||
localSessionDescription = session;
|
||||
log("Send SDP to remote peer");
|
||||
conn.send(JSON.stringify({"id": "sdp", "data": session}));
|
||||
});
|
||||
}).catch(log);
|
||||
}
|
||||
}
|
||||