recode game window, improve mouse lock handling, improve debug overlay, version 1.1.2
This commit is contained in:
@@ -1,22 +1,48 @@
|
|||||||
|
import Minecraft from "./Minecraft.js";
|
||||||
|
import FocusStateType from "../util/FocusStateType.js";
|
||||||
import GuiIngameMenu from "./gui/screens/GuiIngameMenu.js";
|
import GuiIngameMenu from "./gui/screens/GuiIngameMenu.js";
|
||||||
import Keyboard from "../util/Keyboard.js";
|
import Keyboard from "../util/Keyboard.js";
|
||||||
import Minecraft from "./Minecraft.js";
|
import GuiLoadingScreen from "./gui/screens/GuiLoadingScreen.js";
|
||||||
|
|
||||||
export default class GameWindow {
|
export default class GameWindow {
|
||||||
|
|
||||||
constructor(minecraft, canvasWrapperId) {
|
constructor(minecraft, canvasWrapperId) {
|
||||||
this.minecraft = minecraft;
|
this.minecraft = minecraft;
|
||||||
this.canvasWrapperId = canvasWrapperId;
|
|
||||||
|
this.width = 0;
|
||||||
|
this.height = 0;
|
||||||
|
|
||||||
|
this.mouseX = 0;
|
||||||
|
this.mouseY = 0;
|
||||||
|
|
||||||
this.mouseMotionX = 0;
|
this.mouseMotionX = 0;
|
||||||
this.mouseMotionY = 0;
|
this.mouseMotionY = 0;
|
||||||
this.mouseLocked = false;
|
|
||||||
this.actualMouseLocked = false;
|
|
||||||
|
|
||||||
this.isMobile = this.detectTouchDevice();
|
this.mouseInsideWindow = false;
|
||||||
|
|
||||||
|
this.mouseDownInterval = null;
|
||||||
|
this.focusState = FocusStateType.EXITED;
|
||||||
|
this.lastIngameSwitchTime = 0;
|
||||||
|
|
||||||
|
this.mobileDevice = this.detectTouchDevice();
|
||||||
|
|
||||||
|
// Initialize canvas elements
|
||||||
|
this.initializeElements(canvasWrapperId);
|
||||||
|
|
||||||
|
// Register listeners
|
||||||
|
if (this.mobileDevice) {
|
||||||
|
this.registerMobileListeners();
|
||||||
|
} else {
|
||||||
|
this.registerDesktopListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create keyboard
|
||||||
|
Keyboard.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeElements(canvasWrapperId) {
|
||||||
// Get canvas wrapper
|
// Get canvas wrapper
|
||||||
this.wrapper = document.getElementById(this.canvasWrapperId);
|
this.wrapper = document.getElementById(canvasWrapperId);
|
||||||
|
|
||||||
// Remove all children of wrapper
|
// Remove all children of wrapper
|
||||||
while (this.wrapper.firstChild) {
|
while (this.wrapper.firstChild) {
|
||||||
@@ -29,143 +55,193 @@ export default class GameWindow {
|
|||||||
|
|
||||||
// Create screen renderer
|
// Create screen renderer
|
||||||
this.canvas2d = document.createElement('canvas');
|
this.canvas2d = document.createElement('canvas');
|
||||||
|
this.canvas2d.debugCanvas = document.createElement('canvas');
|
||||||
this.wrapper.appendChild(this.canvas2d);
|
this.wrapper.appendChild(this.canvas2d);
|
||||||
|
|
||||||
// Create screen item renderer
|
// Create screen item renderer
|
||||||
this.canvasItems = document.createElement('canvas');
|
this.canvasItems = document.createElement('canvas');
|
||||||
this.wrapper.appendChild(this.canvasItems);
|
this.wrapper.appendChild(this.canvasItems);
|
||||||
|
}
|
||||||
|
|
||||||
this.canvas.addEventListener("webglcontextlost", function (event) {
|
registerDesktopListeners() {
|
||||||
event.preventDefault();
|
this.registerListener(window, 'resize', event => {
|
||||||
}, false);
|
this.updateWindowSize();
|
||||||
|
|
||||||
let mouseDownInterval = null;
|
|
||||||
|
|
||||||
// Request focus
|
|
||||||
document.onclick = () => {
|
|
||||||
if (this.minecraft.currentScreen === null) {
|
|
||||||
this.requestFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener('resize', _ => this.updateWindowSize(), false);
|
|
||||||
|
|
||||||
// Focus listener
|
|
||||||
document.addEventListener('pointerlockchange', _ => this.onFocusChanged(), false);
|
|
||||||
document.addEventListener('pointerlockerror', e => {
|
|
||||||
e.preventDefault()
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
// Mouse motion
|
|
||||||
document.addEventListener('mousemove', event => {
|
|
||||||
this.onMouseMove(event);
|
|
||||||
|
|
||||||
// Handle mouse move on screen
|
|
||||||
if (minecraft.currentScreen !== null) {
|
|
||||||
minecraft.currentScreen.mouseDragged(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code);
|
|
||||||
}
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
// Mouse release
|
|
||||||
document.addEventListener('mouseup', event => {
|
|
||||||
// Handle mouse release on screen
|
|
||||||
if (minecraft.currentScreen !== null) {
|
|
||||||
minecraft.currentScreen.mouseReleased(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearInterval(mouseDownInterval);
|
|
||||||
}, false);
|
|
||||||
|
|
||||||
// Losing focus event
|
|
||||||
this.canvas.addEventListener("mouseout", () => {
|
|
||||||
if (minecraft.currentScreen === null && !this.actualMouseLocked) {
|
|
||||||
minecraft.displayScreen(new GuiIngameMenu());
|
|
||||||
}
|
|
||||||
|
|
||||||
clearInterval(mouseDownInterval);
|
|
||||||
});
|
});
|
||||||
|
this.registerListener(document, 'mousedown', event => {
|
||||||
|
// In-Game mouse click
|
||||||
|
this.minecraft.onMouseClicked(event.button);
|
||||||
|
|
||||||
// Right click
|
// Start interval to repeat the mouse event
|
||||||
document.addEventListener('contextmenu', event => {
|
if (this.mouseDownInterval !== null) {
|
||||||
event.preventDefault();
|
clearInterval(this.mouseDownInterval);
|
||||||
});
|
|
||||||
|
|
||||||
// Mouse buttons
|
|
||||||
document.addEventListener('mousedown', event => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
// Create sound engine (It has to be created after user interaction)
|
|
||||||
if (!minecraft.soundManager.isCreated()) {
|
|
||||||
minecraft.soundManager.create(minecraft.worldRenderer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle in-game mouse click
|
|
||||||
if (!this.isMobile) {
|
|
||||||
minecraft.onMouseClicked(event.button);
|
|
||||||
|
|
||||||
// Start interval to repeat the mouse event
|
|
||||||
clearInterval(mouseDownInterval);
|
|
||||||
mouseDownInterval = setInterval(() => minecraft.onMouseClicked(event.button), 250);
|
|
||||||
}
|
}
|
||||||
|
this.mouseDownInterval = setInterval(_ => this.minecraft.onMouseClicked(event.button), 250);
|
||||||
|
|
||||||
// Handle mouse click on screen
|
// Handle mouse click on screen
|
||||||
if (minecraft.currentScreen !== null) {
|
let currentScreen = this.minecraft.currentScreen;
|
||||||
minecraft.currentScreen.mouseClicked(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code);
|
if (currentScreen !== null) {
|
||||||
|
currentScreen.mouseClicked(
|
||||||
|
event.x / this.scaleFactor,
|
||||||
|
event.y / this.scaleFactor,
|
||||||
|
event.code
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, false);
|
|
||||||
|
|
||||||
// Mouse scroll
|
// Fix cursor lock state
|
||||||
this.wrapper.addEventListener('wheel', (event) => {
|
this.requestCursorUpdate();
|
||||||
event.preventDefault();
|
|
||||||
|
// Request lock on click
|
||||||
|
if (this.minecraft.currentScreen === null && this.focusState === FocusStateType.EXITED) {
|
||||||
|
this.updateFocusState(FocusStateType.REQUEST_LOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initialSoundEngine();
|
||||||
|
});
|
||||||
|
this.registerListener(document, 'mousemove', event => {
|
||||||
|
this.mouseX = event.clientX / this.scaleFactor;
|
||||||
|
this.mouseY = event.clientY / this.scaleFactor;
|
||||||
|
|
||||||
|
this.mouseMotionX = event.movementX;
|
||||||
|
this.mouseMotionY = -event.movementY;
|
||||||
|
|
||||||
|
// Handle mouse move on screen
|
||||||
|
let currentScreen = this.minecraft.currentScreen;
|
||||||
|
if (currentScreen !== null) {
|
||||||
|
currentScreen.mouseDragged(event.x / this.scaleFactor, event.y / this.scaleFactor, event.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requestCursorUpdate();
|
||||||
|
});
|
||||||
|
this.registerListener(document, 'mouseup', event => {
|
||||||
|
// Handle mouse release on screen
|
||||||
|
let currentScreen = this.minecraft.currentScreen;
|
||||||
|
if (currentScreen !== null) {
|
||||||
|
currentScreen.mouseReleased(
|
||||||
|
event.x / this.scaleFactor,
|
||||||
|
event.y / this.scaleFactor,
|
||||||
|
event.code
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.mouseDownInterval !== null) {
|
||||||
|
clearInterval(this.mouseDownInterval);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.registerListener(document, 'pointerlockchange', event => {
|
||||||
|
let intentState = this.focusState.getIntent(); // Get target state we want to switch into
|
||||||
|
let isCursorLocked = this.isCursorLockedToCanvas(); // Get current state of the canvas lock
|
||||||
|
let isLockIntent = intentState === FocusStateType.LOCKED; // Check if we want to lock the cursor
|
||||||
|
|
||||||
|
let lastSwitchDuration = Date.now() - this.lastIngameSwitchTime;
|
||||||
|
if (this.focusState === FocusStateType.LOCKED && !isCursorLocked && lastSwitchDuration < 200) {
|
||||||
|
// If the user exists the inventory by using the escape key, the cursor unlocks from the canvas,
|
||||||
|
// so we have to prevent that by switching immediately to the request state
|
||||||
|
this.focusState = FocusStateType.REQUEST_LOCK;
|
||||||
|
} else {
|
||||||
|
if (intentState === null) {
|
||||||
|
// The state changed unintentionally, so we have to choose a new state from the current canvas lock
|
||||||
|
this.updateFocusState(isCursorLocked ? FocusStateType.LOCKED : FocusStateType.EXITED);
|
||||||
|
} else if (isCursorLocked === isLockIntent) {
|
||||||
|
// Check if the canvas completed the lock operation like intended and change the state to its final state
|
||||||
|
this.updateFocusState(intentState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.registerListener(this.wrapper, 'mouseover', event => {
|
||||||
|
// Enable keyboard util handling
|
||||||
|
Keyboard.setEnabled(true);
|
||||||
|
this.mouseInsideWindow = true;
|
||||||
|
|
||||||
|
// Update cursor lock
|
||||||
|
this.requestCursorUpdate();
|
||||||
|
});
|
||||||
|
this.registerListener(this.wrapper, 'mouseleave', event => {
|
||||||
|
// Disable keyboard util handling
|
||||||
|
Keyboard.setEnabled(false);
|
||||||
|
this.mouseInsideWindow = false;
|
||||||
|
|
||||||
|
// Update cursor lock
|
||||||
|
this.requestCursorUpdate();
|
||||||
|
});
|
||||||
|
this.registerListener(document, 'mouseout', event => {
|
||||||
|
this.requestCursorUpdate();
|
||||||
|
});
|
||||||
|
this.registerListener(document, 'mouseenter', event => {
|
||||||
|
this.requestCursorUpdate();
|
||||||
|
});
|
||||||
|
this.registerListener(window, 'keydown', event => {
|
||||||
|
// Prevent browser functions except fullscreen
|
||||||
|
if (event.key !== 'F11') {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore key input if mouse is not inside window
|
||||||
|
if (!this.mouseInsideWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle escape press if focus is still in requesting state
|
||||||
|
if (event.key === 'Escape' && this.minecraft.currentScreen === null) {
|
||||||
|
this.updateFocusState(FocusStateType.REQUEST_EXIT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let currentScreen = this.minecraft.currentScreen;
|
||||||
|
if (currentScreen === null) {
|
||||||
|
// Handle in-game key press
|
||||||
|
this.minecraft.onKeyPressed(event.code);
|
||||||
|
} else {
|
||||||
|
// Handle key type on screen
|
||||||
|
currentScreen.keyTyped(event.code, event.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.requestCursorUpdate();
|
||||||
|
}, false);
|
||||||
|
this.registerListener(window, 'keyup', event => {
|
||||||
|
// Handle key release on screen
|
||||||
|
let currentScreen = this.minecraft.currentScreen;
|
||||||
|
if (currentScreen !== null) {
|
||||||
|
currentScreen.keyReleased(event.code);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.registerListener(document, 'contextmenu');
|
||||||
|
this.registerListener(this.wrapper, 'wheel', event => {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
|
// Handle mouse scroll
|
||||||
let delta = Math.sign(event.deltaY);
|
let delta = Math.sign(event.deltaY);
|
||||||
minecraft.onMouseScroll(delta);
|
this.minecraft.onMouseScroll(delta);
|
||||||
}, false);
|
|
||||||
|
|
||||||
// Keyboard interaction with screen
|
|
||||||
window.addEventListener('keydown', event => {
|
|
||||||
if (event.code === "F11") {
|
|
||||||
return; // Toggle fullscreen
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prevent key
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (minecraft.currentScreen !== null) {
|
|
||||||
// Handle key type on screen
|
|
||||||
minecraft.currentScreen.keyTyped(event.code, event.key);
|
|
||||||
} else if (event.code === 'Escape') {
|
|
||||||
minecraft.displayScreen(new GuiIngameMenu());
|
|
||||||
} else {
|
|
||||||
minecraft.onKeyPressed(event.code);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Keyboard interaction with screen
|
registerMobileListeners() {
|
||||||
window.addEventListener('keyup', (event) => {
|
let touchStartTime = 0;
|
||||||
// Prevent key
|
let prevTouched = false;
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
if (minecraft.currentScreen !== null) {
|
this.registerListener(window, 'resize', event => {
|
||||||
// Handle key release on screen
|
this.updateWindowSize();
|
||||||
minecraft.currentScreen.keyReleased(event.code);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
this.registerListener(document, 'touchstart', event => {
|
||||||
// Touch interaction
|
|
||||||
let touchStart;
|
|
||||||
window.addEventListener('touchstart', (event) => {
|
|
||||||
for (let i = 0; i < event.touches.length; i++) {
|
for (let i = 0; i < event.touches.length; i++) {
|
||||||
let touch = event.touches[i];
|
let touch = event.touches[i];
|
||||||
|
|
||||||
let x = touch.pageX;
|
let x = touch.pageX;
|
||||||
let y = touch.pageY;
|
let y = touch.pageY;
|
||||||
|
|
||||||
|
// Handle mouse click on screen
|
||||||
|
let currentScreen = this.minecraft.currentScreen;
|
||||||
|
if (currentScreen !== null) {
|
||||||
|
currentScreen.mouseClicked(
|
||||||
|
x / this.scaleFactor,
|
||||||
|
y / this.scaleFactor,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let isRightHand = x > this.wrapper.offsetWidth / 2;
|
let isRightHand = x > this.wrapper.offsetWidth / 2;
|
||||||
|
|
||||||
|
// Handle player movement
|
||||||
if (isRightHand) {
|
if (isRightHand) {
|
||||||
touchStart = Date.now();
|
touchStartTime = Date.now();
|
||||||
} else {
|
} else {
|
||||||
let tileSize = this.wrapper.offsetWidth / 8;
|
let tileSize = this.wrapper.offsetWidth / 8;
|
||||||
|
|
||||||
@@ -201,48 +277,61 @@ export default class GameWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, false);
|
||||||
// Create sound engine (It has to be created after user interaction)
|
this.registerListener(document, 'touchmove', event => {
|
||||||
if (!minecraft.soundManager.isCreated()) {
|
|
||||||
minecraft.soundManager.create(minecraft.worldRenderer);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Touch movement
|
|
||||||
let prevTouch;
|
|
||||||
window.addEventListener('touchmove', (event) => {
|
|
||||||
for (let i = 0; i < event.touches.length; i++) {
|
for (let i = 0; i < event.touches.length; i++) {
|
||||||
let touch = event.touches[i];
|
let touch = event.touches[i];
|
||||||
|
|
||||||
let x = touch.pageX;
|
let x = touch.pageX;
|
||||||
let y = touch.pageY;
|
let y = touch.pageY;
|
||||||
|
|
||||||
|
// Handle mouse move on screen
|
||||||
|
let currentScreen = this.minecraft.currentScreen;
|
||||||
|
if (currentScreen !== null) {
|
||||||
|
currentScreen.mouseDragged(
|
||||||
|
x / this.scaleFactor,
|
||||||
|
y / this.scaleFactor,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Right hand
|
// Right hand
|
||||||
let isRightHand = x > this.wrapper.offsetWidth / 2;
|
let isRightHand = x > this.wrapper.offsetWidth / 2;
|
||||||
|
|
||||||
|
// Handle player movement
|
||||||
if (isRightHand) {
|
if (isRightHand) {
|
||||||
// Player movement
|
if (prevTouched) {
|
||||||
if (prevTouch) {
|
this.mouseMotionX = (x - prevTouched.pageX) * 10;
|
||||||
this.mouseMotionX = (x - prevTouch.pageX) * 10;
|
this.mouseMotionY = -(y - prevTouched.pageY) * 10;
|
||||||
this.mouseMotionY = -(y - prevTouch.pageY) * 10;
|
|
||||||
}
|
}
|
||||||
|
prevTouched = touch;
|
||||||
prevTouch = touch;
|
touchStartTime = Date.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}, false);
|
||||||
window.addEventListener('touchend', (event) => {
|
this.registerListener(document, 'touchend', event => {
|
||||||
// Break block
|
// Break block
|
||||||
if (!prevTouch && touchStart && (Date.now() - touchStart) < 1000) {
|
if (!prevTouched && touchStartTime !== 0 && (Date.now() - touchStartTime) < 1000) {
|
||||||
minecraft.onMouseClicked(2);
|
this.minecraft.onMouseClicked(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
prevTouch = null;
|
prevTouched = false;
|
||||||
touchStart = null;
|
touchStartTime = 0;
|
||||||
|
|
||||||
// Stop pressing keys
|
// Handle touches
|
||||||
for (let i = 0; i < event.changedTouches.length; i++) {
|
for (let i = 0; i < event.changedTouches.length; i++) {
|
||||||
let touch = event.changedTouches[i];
|
let touch = event.changedTouches[i];
|
||||||
|
let x = touch.pageX;
|
||||||
|
let y = touch.pageY;
|
||||||
|
|
||||||
|
// Handle mouse release on screen
|
||||||
|
let currentScreen = this.minecraft.currentScreen;
|
||||||
|
if (currentScreen !== null) {
|
||||||
|
currentScreen.mouseReleased(
|
||||||
|
x / this.scaleFactor,
|
||||||
|
y / this.scaleFactor,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Left hand
|
// Left hand
|
||||||
let isLeftHand = touch.pageX < this.wrapper.offsetWidth / 2;
|
let isLeftHand = touch.pageX < this.wrapper.offsetWidth / 2;
|
||||||
@@ -253,41 +342,18 @@ export default class GameWindow {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
this.initialSoundEngine();
|
||||||
|
}, false);
|
||||||
|
this.registerListener(document, 'contextmenu');
|
||||||
|
|
||||||
// Break block listener
|
// Break block listener
|
||||||
if (this.isMobile) {
|
setInterval(() => {
|
||||||
setInterval(() => {
|
if (touchStartTime !== 0 && (Date.now() - touchStartTime) > 250) {
|
||||||
if (touchStart && (Date.now() - touchStart) > 1000) {
|
touchStartTime = Date.now();
|
||||||
touchStart = Date.now();
|
this.minecraft.onMouseClicked(0);
|
||||||
minecraft.onMouseClicked(0);
|
}
|
||||||
}
|
}, 200);
|
||||||
}, 200);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create keyboard
|
|
||||||
Keyboard.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
requestFocus() {
|
|
||||||
if (this.isMobile) {
|
|
||||||
document.body.requestFullscreen();
|
|
||||||
} else {
|
|
||||||
window.focus();
|
|
||||||
this.canvas.requestPointerLock();
|
|
||||||
document.body.style.cursor = 'none';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mouseLocked = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
exitFocus() {
|
|
||||||
if (this.isMobile) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.exitPointerLock();
|
|
||||||
document.body.style.cursor = 'default';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateWindowSize() {
|
updateWindowSize() {
|
||||||
@@ -313,11 +379,15 @@ export default class GameWindow {
|
|||||||
this.canvas2d.style.width = wrapperWidth + "px";
|
this.canvas2d.style.width = wrapperWidth + "px";
|
||||||
this.canvas2d.style.height = wrapperHeight + "px";
|
this.canvas2d.style.height = wrapperHeight + "px";
|
||||||
|
|
||||||
|
let debugCanvas = this.canvas2d["debugCanvas"];
|
||||||
|
debugCanvas.width = this.canvas2d.width;
|
||||||
|
debugCanvas.height = this.canvas2d.height;
|
||||||
|
|
||||||
// Reinitialize gui
|
// Reinitialize gui
|
||||||
this.minecraft.screenRenderer.initialize();
|
this.minecraft.screenRenderer.initialize();
|
||||||
|
|
||||||
// Reinitialize current screen
|
// Reinitialize current screen
|
||||||
if (!(this.minecraft.currentScreen === null)) {
|
if (this.minecraft.currentScreen !== null) {
|
||||||
this.minecraft.currentScreen.setup(this.minecraft, this.width, this.height);
|
this.minecraft.currentScreen.setup(this.minecraft, this.width, this.height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -332,29 +402,63 @@ export default class GameWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.scaleFactor = scale;
|
this.scaleFactor = scale;
|
||||||
this.width = wrapperWidth / scale;
|
this.width = Math.ceil(wrapperWidth / scale);
|
||||||
this.height = wrapperHeight / scale;
|
this.height = Math.ceil(wrapperHeight / scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
onFocusChanged() {
|
isCursorLockedToCanvas() {
|
||||||
this.actualMouseLocked = document.pointerLockElement === this.canvas;
|
// The actual state of the browser cursor lock
|
||||||
|
return document.pointerLockElement === this.canvas;
|
||||||
}
|
}
|
||||||
|
|
||||||
onMouseMove(event) {
|
isLocked() {
|
||||||
this.mouseX = event.clientX / this.scaleFactor;
|
// The actual definition for the game if the cursor is locked or not
|
||||||
this.mouseY = event.clientY / this.scaleFactor;
|
return this.focusState.isLock() && this.minecraft.currentScreen === null;
|
||||||
|
}
|
||||||
|
|
||||||
if (document.pointerLockElement !== this.canvas) {
|
updateFocusState(state) {
|
||||||
this.mouseLocked = false;
|
if (state.getIntent() === this.focusState || state === this.focusState) {
|
||||||
|
return;
|
||||||
if (this.minecraft.currentScreen === null) {
|
|
||||||
this.requestFocus();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.actualMouseLocked || this.mouseLocked) {
|
let prevLock = this.focusState.isLock();
|
||||||
this.mouseMotionX = event.movementX;
|
let nextLock = state.isLock();
|
||||||
this.mouseMotionY = -event.movementY;
|
|
||||||
|
// Update state
|
||||||
|
this.focusState = state;
|
||||||
|
|
||||||
|
// Update cursor visibility
|
||||||
|
document.body.style.cursor = nextLock ? 'none' : 'default';
|
||||||
|
|
||||||
|
// Request lock state
|
||||||
|
this.requestCursorUpdate();
|
||||||
|
|
||||||
|
// Open menu on exit
|
||||||
|
if (prevLock !== nextLock) {
|
||||||
|
let currentScreen = this.minecraft.currentScreen;
|
||||||
|
|
||||||
|
// Open in-game menu
|
||||||
|
if (currentScreen === null && !nextLock) {
|
||||||
|
this.minecraft.displayScreen(new GuiIngameMenu());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close current screen
|
||||||
|
if (!(currentScreen instanceof GuiLoadingScreen) && nextLock) {
|
||||||
|
this.minecraft.displayScreen(null);
|
||||||
|
this.lastIngameSwitchTime = Date.now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestCursorUpdate() {
|
||||||
|
// Check if the current state doesn't match the canvas lock
|
||||||
|
if (this.mouseInsideWindow && this.focusState.isLock() !== this.isCursorLockedToCanvas()) {
|
||||||
|
// Request cursor lock depending on the state
|
||||||
|
if (this.focusState.isLock()) {
|
||||||
|
this.canvas.requestPointerLock();
|
||||||
|
} else {
|
||||||
|
document.exitPointerLock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,8 +471,30 @@ export default class GameWindow {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
getMemoryLimit() {
|
||||||
this.openUrl(Minecraft.URL_GITHUB);
|
return this.getMemoryValue("jsHeapSizeLimit", 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMemoryAllocated() {
|
||||||
|
return this.getMemoryValue("totalJSHeapSize", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMemoryUsed() {
|
||||||
|
return this.getMemoryValue("usedJSHeapSize", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
getMemoryValue(key, fallbackValue = 0) {
|
||||||
|
let performance = window.performance || window.msPerformance || window.webkitPerformance || window.mozPerformance;
|
||||||
|
if (performance && performance.memory && performance.memory[key]) {
|
||||||
|
return performance.memory[key];
|
||||||
|
}
|
||||||
|
return fallbackValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
getGPUName() {
|
||||||
|
let gl = this.canvas.getContext("webgl2");
|
||||||
|
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
|
||||||
|
return gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL);
|
||||||
}
|
}
|
||||||
|
|
||||||
openUrl(url, newTab) {
|
openUrl(url, newTab) {
|
||||||
@@ -379,8 +505,46 @@ export default class GameWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.openUrl(Minecraft.URL_GITHUB);
|
||||||
|
}
|
||||||
|
|
||||||
async getClipboardText() {
|
async getClipboardText() {
|
||||||
return navigator.clipboard.readText();
|
return navigator.clipboard.readText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isMobileDevice() {
|
||||||
|
return this.mobileDevice;
|
||||||
|
}
|
||||||
|
|
||||||
|
pullMouseMotionX() {
|
||||||
|
let value = this.mouseMotionX;
|
||||||
|
this.mouseMotionX = 0;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
pullMouseMotionY() {
|
||||||
|
let value = this.mouseMotionY;
|
||||||
|
this.mouseMotionY = 0;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
initialSoundEngine() {
|
||||||
|
// Create sound engine (It has to be created after user interaction)
|
||||||
|
if (!this.minecraft.soundManager.isCreated()) {
|
||||||
|
this.minecraft.soundManager.create(this.minecraft.worldRenderer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerListener(parent, event, listener = null, preventDefaults = true) {
|
||||||
|
parent.addEventListener(event, event => {
|
||||||
|
if (preventDefaults) {
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (listener !== null) {
|
||||||
|
listener(event);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,10 +21,11 @@ import CommandHandler from "./command/CommandHandler.js";
|
|||||||
import GuiContainerCreative from "./gui/screens/container/GuiContainerCreative.js";
|
import GuiContainerCreative from "./gui/screens/container/GuiContainerCreative.js";
|
||||||
import GameProfile from "../util/GameProfile.js";
|
import GameProfile from "../util/GameProfile.js";
|
||||||
import UUID from "../util/UUID.js";
|
import UUID from "../util/UUID.js";
|
||||||
|
import FocusStateType from "../util/FocusStateType.js";
|
||||||
|
|
||||||
export default class Minecraft {
|
export default class Minecraft {
|
||||||
|
|
||||||
static VERSION = "1.1.1"
|
static VERSION = "1.1.2"
|
||||||
static URL_GITHUB = "https://github.com/labystudio/js-minecraft";
|
static URL_GITHUB = "https://github.com/labystudio/js-minecraft";
|
||||||
static PROTOCOL_VERSION = 758;
|
static PROTOCOL_VERSION = 758;
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ export default class Minecraft {
|
|||||||
this.player = null;
|
this.player = null;
|
||||||
|
|
||||||
this.fps = 0;
|
this.fps = 0;
|
||||||
|
this.maxFps = 0;
|
||||||
|
|
||||||
let username = "Player" + Math.floor(Math.random() * 100);
|
let username = "Player" + Math.floor(Math.random() * 100);
|
||||||
this.profile = new GameProfile(username, UUID.randomUUID());
|
this.profile = new GameProfile(username, UUID.randomUUID());
|
||||||
@@ -137,7 +139,7 @@ export default class Minecraft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasInGameFocus() {
|
hasInGameFocus() {
|
||||||
return this.window.mouseLocked && this.currentScreen === null;
|
return this.window.isLocked() && this.currentScreen === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
isInGame() {
|
isInGame() {
|
||||||
@@ -167,7 +169,7 @@ export default class Minecraft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render the game
|
// Render the game
|
||||||
this.onRender(this.timer.partialTicks);
|
this.onRender(this.isPaused() ? 0 : this.timer.partialTicks);
|
||||||
|
|
||||||
// Increase rendered frame
|
// Increase rendered frame
|
||||||
this.frames++;
|
this.frames++;
|
||||||
@@ -175,6 +177,7 @@ export default class Minecraft {
|
|||||||
// Loop if a second passed
|
// Loop if a second passed
|
||||||
while (Date.now() >= this.lastTime + 1000) {
|
while (Date.now() >= this.lastTime + 1000) {
|
||||||
this.fps = this.frames;
|
this.fps = this.frames;
|
||||||
|
this.maxFps = Math.max(this.maxFps, this.fps);
|
||||||
this.lastTime += 1000;
|
this.lastTime += 1000;
|
||||||
this.frames = 0;
|
this.frames = 0;
|
||||||
}
|
}
|
||||||
@@ -184,10 +187,9 @@ export default class Minecraft {
|
|||||||
if (this.isInGame()) {
|
if (this.isInGame()) {
|
||||||
// Player rotation
|
// Player rotation
|
||||||
if (!this.isPaused()) {
|
if (!this.isPaused()) {
|
||||||
this.player.turn(this.window.mouseMotionX, this.window.mouseMotionY);
|
let deltaX = this.window.pullMouseMotionX();
|
||||||
|
let deltaY = this.window.pullMouseMotionY();
|
||||||
this.window.mouseMotionX = 0;
|
this.player.turn(deltaX, deltaY);
|
||||||
this.window.mouseMotionY = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update lights
|
// Update lights
|
||||||
@@ -196,7 +198,7 @@ export default class Minecraft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render the game
|
// Render the game
|
||||||
if (this.hasInGameFocus()) {
|
if (this.isInGame() && !this.isPaused()) {
|
||||||
this.worldRenderer.render(partialTicks);
|
this.worldRenderer.render(partialTicks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +209,10 @@ export default class Minecraft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
displayScreen(screen) {
|
displayScreen(screen) {
|
||||||
|
if (screen === this.currentScreen) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof screen === "undefined") {
|
if (typeof screen === "undefined") {
|
||||||
console.error("Tried to display an undefined screen");
|
console.error("Tried to display an undefined screen");
|
||||||
return;
|
return;
|
||||||
@@ -230,9 +236,9 @@ export default class Minecraft {
|
|||||||
|
|
||||||
// Initialize new screen
|
// Initialize new screen
|
||||||
if (screen === null) {
|
if (screen === null) {
|
||||||
this.window.requestFocus();
|
this.window.updateFocusState(FocusStateType.REQUEST_LOCK);
|
||||||
} else {
|
} else {
|
||||||
this.window.exitFocus();
|
this.window.updateFocusState(FocusStateType.REQUEST_EXIT);
|
||||||
screen.setup(this, this.window.width, this.window.height);
|
screen.setup(this, this.window.width, this.window.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,6 +248,9 @@ export default class Minecraft {
|
|||||||
|
|
||||||
onTick() {
|
onTick() {
|
||||||
if (this.isInGame() && !this.isPaused()) {
|
if (this.isInGame() && !this.isPaused()) {
|
||||||
|
// Tick overlay
|
||||||
|
this.ingameOverlay.onTick();
|
||||||
|
|
||||||
// Tick world
|
// Tick world
|
||||||
this.world.onTick();
|
this.world.onTick();
|
||||||
|
|
||||||
@@ -253,9 +262,6 @@ export default class Minecraft {
|
|||||||
|
|
||||||
// Tick particle renderer
|
// Tick particle renderer
|
||||||
this.particleRenderer.onTick();
|
this.particleRenderer.onTick();
|
||||||
|
|
||||||
// Tick overlay
|
|
||||||
this.ingameOverlay.onTick();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tick the screen
|
// Tick the screen
|
||||||
@@ -315,6 +321,7 @@ export default class Minecraft {
|
|||||||
// Toggle debug overlay
|
// Toggle debug overlay
|
||||||
if (button === "F3") {
|
if (button === "F3") {
|
||||||
this.settings.debugOverlay = !this.settings.debugOverlay;
|
this.settings.debugOverlay = !this.settings.debugOverlay;
|
||||||
|
this.settings.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open inventory
|
// Open inventory
|
||||||
@@ -324,7 +331,7 @@ export default class Minecraft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onMouseClicked(button) {
|
onMouseClicked(button) {
|
||||||
if (this.window.mouseLocked) {
|
if (this.window.isLocked()) {
|
||||||
let hitResult = this.player.rayTrace(5, this.timer.partialTicks);
|
let hitResult = this.player.rayTrace(5, this.timer.partialTicks);
|
||||||
|
|
||||||
// Destroy block
|
// Destroy block
|
||||||
|
|||||||
@@ -195,4 +195,12 @@ export default class Entity {
|
|||||||
this.isDead = true;
|
this.isDead = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isMoving() {
|
||||||
|
return this.motionX !== 0.0
|
||||||
|
|| this.motionY !== 0.0 && !this.onGround
|
||||||
|
|| this.motionZ !== 0.0
|
||||||
|
|| this.rotationYaw !== this.prevRotationYaw
|
||||||
|
|| this.rotationPitch !== this.prevRotationPitch;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,8 +16,8 @@ export default class Gui {
|
|||||||
this.minecraft.fontRenderer.drawString(stack, string, x - this.getStringWidth(stack, string) / 2, y, color);
|
this.minecraft.fontRenderer.drawString(stack, string, x - this.getStringWidth(stack, string) / 2, y, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawRightString(stack, string, x, y, color = -1) {
|
drawRightString(stack, string, x, y, color = -1, shadow = true) {
|
||||||
this.minecraft.fontRenderer.drawString(stack, string, x - this.getStringWidth(stack, string), y, color);
|
this.minecraft.fontRenderer.drawString(stack, string, x - this.getStringWidth(stack, string), y, color, shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
drawString(stack, string, x, y, color = -1, shadow = true) {
|
drawString(stack, string, x, y, color = -1, shadow = true) {
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ import Gui from "../Gui.js";
|
|||||||
import Block from "../../world/block/Block.js";
|
import Block from "../../world/block/Block.js";
|
||||||
import ChatOverlay from "./ChatOverlay.js";
|
import ChatOverlay from "./ChatOverlay.js";
|
||||||
import Minecraft from "../../Minecraft.js";
|
import Minecraft from "../../Minecraft.js";
|
||||||
|
import EnumBlockFace from "../../../util/EnumBlockFace.js";
|
||||||
|
import MathHelper from "../../../util/MathHelper.js";
|
||||||
|
import FontRenderer from "../../render/gui/FontRenderer.js";
|
||||||
|
import EnumSkyBlock from "../../../util/EnumSkyBlock.js";
|
||||||
|
|
||||||
export default class IngameOverlay extends Gui {
|
export default class IngameOverlay extends Gui {
|
||||||
|
|
||||||
@@ -14,6 +18,8 @@ export default class IngameOverlay extends Gui {
|
|||||||
|
|
||||||
this.textureCrosshair = minecraft.resources["gui/icons.png"];
|
this.textureCrosshair = minecraft.resources["gui/icons.png"];
|
||||||
this.textureHotbar = minecraft.resources["gui/gui.png"];
|
this.textureHotbar = minecraft.resources["gui/gui.png"];
|
||||||
|
|
||||||
|
this.ticksRendered = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
render(stack, mouseX, mouseY, partialTicks) {
|
render(stack, mouseX, mouseY, partialTicks) {
|
||||||
@@ -28,29 +34,32 @@ export default class IngameOverlay extends Gui {
|
|||||||
// Render chat
|
// Render chat
|
||||||
this.chatOverlay.render(stack, mouseX, mouseY, partialTicks);
|
this.chatOverlay.render(stack, mouseX, mouseY, partialTicks);
|
||||||
|
|
||||||
let world = this.minecraft.world;
|
// Render debug canvas on stack
|
||||||
let player = this.minecraft.player;
|
|
||||||
|
|
||||||
let x = Math.floor(player.x);
|
|
||||||
let y = Math.floor(player.y);
|
|
||||||
let z = Math.floor(player.z);
|
|
||||||
|
|
||||||
let fps = Math.floor(this.minecraft.fps);
|
|
||||||
let lightUpdates = world.lightUpdateQueue.length;
|
|
||||||
let chunkUpdates = this.minecraft.worldRenderer.chunkSectionUpdateQueue.length;
|
|
||||||
let lightLevel = world.getTotalLightAt(x, y, z);
|
|
||||||
|
|
||||||
// Debug
|
|
||||||
if (this.minecraft.settings.debugOverlay) {
|
if (this.minecraft.settings.debugOverlay) {
|
||||||
this.drawString(stack, "js-minecraft " + Minecraft.VERSION, 1, 1);
|
stack.drawImage(this.window.canvas2d.debugCanvas, 0, 0);
|
||||||
this.drawString(stack, fps + " fps," + " " + lightUpdates + " light updates," + " " + chunkUpdates + " chunk updates", 1, 1 + 9 * 1);
|
|
||||||
this.drawString(stack, x + ", " + y + ", " + z + " (" + (x >> 4) + ", " + (y >> 4) + ", " + (z >> 4) + ")", 1, 1 + 9 * 2);
|
|
||||||
this.drawString(stack, "Light: " + lightLevel, 1, 1 + 9 * 3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onTick() {
|
onTick() {
|
||||||
this.chatOverlay.onTick();
|
this.chatOverlay.onTick();
|
||||||
|
|
||||||
|
// Render debug overlay on tick
|
||||||
|
if (this.minecraft.settings.debugOverlay) {
|
||||||
|
let stack = this.window.canvas2d.debugCanvas.getContext('2d');
|
||||||
|
let moving = this.minecraft.player.isMoving();
|
||||||
|
|
||||||
|
// Render debug overlay each tick if the player is moving
|
||||||
|
if (this.ticksRendered % (moving ? 1 : 20) === 0) {
|
||||||
|
// Clear debug canvas
|
||||||
|
stack.clearRect(0, 0, this.window.width, this.window.height);
|
||||||
|
|
||||||
|
// Render debug information
|
||||||
|
this.renderLeftDebugOverlay(stack);
|
||||||
|
this.renderRightDebugOverlay(stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.ticksRendered++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCrosshair(stack, x, y) {
|
renderCrosshair(stack, x, y) {
|
||||||
@@ -85,4 +94,181 @@ export default class IngameOverlay extends Gui {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderLeftDebugOverlay(stack) {
|
||||||
|
let world = this.minecraft.world;
|
||||||
|
let player = this.minecraft.player;
|
||||||
|
let worldRenderer = this.minecraft.worldRenderer;
|
||||||
|
|
||||||
|
let x = player.x;
|
||||||
|
let y = player.y;
|
||||||
|
let z = player.z;
|
||||||
|
|
||||||
|
let yaw = MathHelper.wrapAngleTo180(player.rotationYaw);
|
||||||
|
let pitch = player.rotationPitch;
|
||||||
|
|
||||||
|
let facingIndex = (((yaw + 180) * 4.0 / 360.0) + 0.5) & 3;
|
||||||
|
let facing = EnumBlockFace.values()[facingIndex + 2];
|
||||||
|
|
||||||
|
let fixedX = x.toFixed(2);
|
||||||
|
let fixedY = y.toFixed(2);
|
||||||
|
let fixedZ = z.toFixed(2);
|
||||||
|
|
||||||
|
let blockX = Math.floor(x);
|
||||||
|
let blockY = Math.floor(y);
|
||||||
|
let blockZ = Math.floor(z);
|
||||||
|
|
||||||
|
let chunkX = blockX >> 4;
|
||||||
|
let chunkY = blockY >> 4;
|
||||||
|
let chunkZ = blockZ >> 4;
|
||||||
|
|
||||||
|
let inChunkX = blockX & 0xF;
|
||||||
|
let inChunkY = blockY & 0xF;
|
||||||
|
let inChunkZ = blockZ & 0xF;
|
||||||
|
|
||||||
|
let visibleChunks = 0;
|
||||||
|
let loadedChunks = 0;
|
||||||
|
for (let [index, chunk] of world.chunks) {
|
||||||
|
for (let y in chunk.sections) {
|
||||||
|
let chunkSection = chunk.sections[y];
|
||||||
|
if (chunkSection.group.visible) {
|
||||||
|
visibleChunks++;
|
||||||
|
}
|
||||||
|
loadedChunks++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let visibleEntities = 0;
|
||||||
|
for (let index in world.entities) {
|
||||||
|
let entity = world.entities[index];
|
||||||
|
if (entity.renderer.group.visible) {
|
||||||
|
visibleEntities++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let fps = Math.floor(this.minecraft.fps);
|
||||||
|
let viewDistance = this.minecraft.settings.viewDistance;
|
||||||
|
let lightUpdates = world.lightUpdateQueue.length;
|
||||||
|
let chunkUpdates = worldRenderer.chunkSectionUpdateQueue.length;
|
||||||
|
let entities = world.entities.length;
|
||||||
|
let particles = this.minecraft.particleRenderer.particles.length;
|
||||||
|
let skyLight = world.getSavedLightValue(EnumSkyBlock.SKY, blockX, blockY, blockZ);
|
||||||
|
let blockLight = world.getSavedLightValue(EnumSkyBlock.BLOCK, blockX, blockY, blockZ);
|
||||||
|
let lightLevel = world.getTotalLightAt(blockX, blockY, blockZ);
|
||||||
|
let biome = "T: " + world.getTemperature(blockX, blockY, blockZ) + " H: " + world.getHumidity(blockX, blockY, blockZ);
|
||||||
|
|
||||||
|
let soundsLoaded = 0;
|
||||||
|
let soundsPlaying = 0;
|
||||||
|
let soundPool = this.minecraft.soundManager.soundPool;
|
||||||
|
for (let [id, sounds] of Object.entries(soundPool)) {
|
||||||
|
for (let sound of sounds) {
|
||||||
|
soundsLoaded++;
|
||||||
|
|
||||||
|
if (sound.isPlaying) {
|
||||||
|
soundsPlaying++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let towards = "Towards " + (facing.isPositive() ? "positive" : "negative") + " " + (facing.isXAxis() ? "X" : "Z");
|
||||||
|
|
||||||
|
let lines = [
|
||||||
|
"js-minecraft " + Minecraft.VERSION,
|
||||||
|
fps + " fps (" + chunkUpdates + " chunk updates) T: " + this.minecraft.maxFps,
|
||||||
|
"C: " + visibleChunks + "/" + loadedChunks + " D: " + viewDistance + ", L: " + lightUpdates,
|
||||||
|
"E: " + visibleEntities + "/" + entities + ", P: " + particles,
|
||||||
|
"",
|
||||||
|
"XYZ: " + fixedX + " / " + fixedY + " / " + fixedZ,
|
||||||
|
"Block: " + blockX + " " + blockY + " " + blockZ,
|
||||||
|
"Chunk: " + chunkX + " " + chunkY + " " + chunkZ + " in " + inChunkX + " " + inChunkY + " " + inChunkZ,
|
||||||
|
"Facing: " + facing.getName() + " (" + towards + ") (" + yaw.toFixed(1) + " / " + pitch.toFixed(1) + ")",
|
||||||
|
"Light: " + lightLevel + " (" + skyLight + " sky, " + blockLight + " block)",
|
||||||
|
// "Biome: " + biome,
|
||||||
|
"",
|
||||||
|
"Sounds: " + soundsPlaying + "/" + soundsLoaded,
|
||||||
|
"Time: " + world.time % 24000 + " (Day " + Math.floor(world.time / 24000) + ")",
|
||||||
|
"Cursor: " + this.minecraft.window.focusState.getName()
|
||||||
|
]
|
||||||
|
|
||||||
|
// Hit result
|
||||||
|
let hit = worldRenderer.lastHitResult;
|
||||||
|
if (hit !== null && hit.type !== 0) {
|
||||||
|
lines.push("Looking at: " + hit.x + " " + hit.y + " " + hit.z);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw lines
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw background
|
||||||
|
this.drawRect(stack,
|
||||||
|
1,
|
||||||
|
1 + FontRenderer.FONT_HEIGHT * i,
|
||||||
|
1 + this.getStringWidth(stack, lines[i]) + 1,
|
||||||
|
1 + FontRenderer.FONT_HEIGHT * i + FontRenderer.FONT_HEIGHT,
|
||||||
|
'#50505090'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw line
|
||||||
|
this.drawString(stack, lines[i], 2, 2 + FontRenderer.FONT_HEIGHT * i, 0xffe0e0e0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderRightDebugOverlay(stack) {
|
||||||
|
let memoryLimit = this.minecraft.window.getMemoryLimit();
|
||||||
|
let memoryUsed = this.minecraft.window.getMemoryUsed();
|
||||||
|
let memoryAllocated = this.minecraft.window.getMemoryAllocated();
|
||||||
|
|
||||||
|
let usedPercentage = Math.floor(memoryUsed / memoryLimit * 100);
|
||||||
|
let allocatedPercentage = Math.floor(memoryAllocated / memoryLimit * 100);
|
||||||
|
|
||||||
|
let width = this.window.canvas.width;
|
||||||
|
let height = this.window.canvas.height;
|
||||||
|
|
||||||
|
let lines = [
|
||||||
|
"Mem: " + usedPercentage + "% " + this.humanFileSize(memoryUsed, memoryLimit),
|
||||||
|
"Allocated: " + allocatedPercentage + "% " + this.humanFileSize(null, memoryAllocated),
|
||||||
|
"",
|
||||||
|
"Display: " + width + "x" + height,
|
||||||
|
this.window.getGPUName()
|
||||||
|
];
|
||||||
|
|
||||||
|
// Draw lines
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
if (lines[i].length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw background
|
||||||
|
this.drawRect(stack,
|
||||||
|
this.window.width - this.getStringWidth(stack, lines[i]) - 3,
|
||||||
|
1 + FontRenderer.FONT_HEIGHT * i,
|
||||||
|
this.window.width - 1,
|
||||||
|
1 + FontRenderer.FONT_HEIGHT * i + FontRenderer.FONT_HEIGHT,
|
||||||
|
'#50505090'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Draw line
|
||||||
|
this.drawRightString(stack, lines[i], this.window.width - 2, 2 + FontRenderer.FONT_HEIGHT * i, 0xffe0e0e0, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
humanFileSize(bytesUsed, bytesMax) {
|
||||||
|
if (Math.abs(bytesMax) < 1000) {
|
||||||
|
return (bytesUsed === null ? "" : bytesUsed + "/") + bytesMax + "B";
|
||||||
|
}
|
||||||
|
const units = ['kB', 'MB'];
|
||||||
|
let u = -1;
|
||||||
|
const r = 10;
|
||||||
|
const thresh = 1000;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (bytesUsed !== null) {
|
||||||
|
bytesUsed /= thresh;
|
||||||
|
}
|
||||||
|
bytesMax /= thresh;
|
||||||
|
++u;
|
||||||
|
} while (Math.round(Math.abs(bytesMax) * r) / r >= thresh && u < units.length - 1);
|
||||||
|
return (bytesUsed === null ? "" : bytesUsed.toFixed(0) + "/") + bytesMax.toFixed(0) + units[u];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,7 @@ export default class GuiContainer extends GuiScreen {
|
|||||||
keyTyped(key, character) {
|
keyTyped(key, character) {
|
||||||
// Swap to slot
|
// Swap to slot
|
||||||
for (let i = 1; i <= 9; i++) {
|
for (let i = 1; i <= 9; i++) {
|
||||||
if (key === 'Digit' + i) {
|
if (key === 'Digit' + i && this.hoverSlot !== null) {
|
||||||
this.container.swapWithHotbar(this.hoverSlot, this.minecraft.player.inventory, i - 1);
|
this.container.swapWithHotbar(this.hoverSlot, this.minecraft.player.inventory, i - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default class GuiLoadingScreen extends GuiScreen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setProgress(progress) {
|
setProgress(progress) {
|
||||||
if (progress < this.progress) {
|
if (progress < this.progress || progress > 1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.progress = progress;
|
this.progress = progress;
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export default class NetworkManager {
|
|||||||
wrapper.write(array);
|
wrapper.write(array);
|
||||||
let chunk = wrapper.getArray().buffer;
|
let chunk = wrapper.getArray().buffer;
|
||||||
|
|
||||||
// Decrypt chunk
|
// Encrypt chunk
|
||||||
if (this.isEncrypted) {
|
if (this.isEncrypted) {
|
||||||
chunk = this.encryption.encrypt(new Uint8Array(chunk));
|
chunk = this.encryption.encrypt(new Uint8Array(chunk));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ export default class WorldRenderer {
|
|||||||
|
|
||||||
this.flushRebuild = false;
|
this.flushRebuild = false;
|
||||||
|
|
||||||
|
this.lastHitResult = null;
|
||||||
|
|
||||||
this.initialize();
|
this.initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -763,6 +765,8 @@ export default class WorldRenderer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.lastHitResult = hitResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
translate(stack, x, y, z) {
|
translate(stack, x, y, z) {
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
export default class ScreenRenderer {
|
export default class ScreenRenderer {
|
||||||
|
|
||||||
static UPSCALE = 3;
|
|
||||||
|
|
||||||
constructor(minecraft, window) {
|
constructor(minecraft, window) {
|
||||||
this.minecraft = minecraft;
|
this.minecraft = minecraft;
|
||||||
this.window = window;
|
this.window = window;
|
||||||
|
|
||||||
|
this.upscale = window.isMobileDevice() ? 1 : 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
initialize() {
|
initialize() {
|
||||||
this.resolution = this.minecraft.isInGame() ? 1 : this.minecraft.window.scaleFactor; // Increase resolution for the splash text
|
this.resolution = this.minecraft.isInGame() ? 1 : this.minecraft.window.scaleFactor; // Increase resolution for the splash text
|
||||||
|
|
||||||
// Update camera size
|
// Update camera size
|
||||||
this.window.canvas2d.width = this.window.width * ScreenRenderer.UPSCALE;
|
this.window.canvas2d.width = this.window.width * this.upscale;
|
||||||
this.window.canvas2d.height = this.window.height * ScreenRenderer.UPSCALE;
|
this.window.canvas2d.height = this.window.height * this.upscale;
|
||||||
|
|
||||||
// Get context stack of 2d canvas
|
// Get context stack of 2d canvas
|
||||||
this.stack2d = this.window.canvas2d.getContext('2d');
|
this.stack2d = this.window.canvas2d.getContext('2d');
|
||||||
@@ -26,7 +26,7 @@ export default class ScreenRenderer {
|
|||||||
let mouseY = this.minecraft.window.mouseY;
|
let mouseY = this.minecraft.window.mouseY;
|
||||||
|
|
||||||
this.stack2d.save();
|
this.stack2d.save();
|
||||||
this.stack2d.scale(ScreenRenderer.UPSCALE, ScreenRenderer.UPSCALE, ScreenRenderer.UPSCALE);
|
this.stack2d.scale(this.upscale, this.upscale, this.upscale);
|
||||||
|
|
||||||
// Reset 2d canvas
|
// Reset 2d canvas
|
||||||
this.stack2d.clearRect(0, 0, this.window.width, this.window.height);
|
this.stack2d.clearRect(0, 0, this.window.width, this.window.height);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default class SoundManager {
|
|||||||
this.audioLoader = new THREE.AudioLoader();
|
this.audioLoader = new THREE.AudioLoader();
|
||||||
this.audioListener = null;
|
this.audioListener = null;
|
||||||
|
|
||||||
this.soundPool = [];
|
this.soundPool = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
create(worldRenderer) {
|
create(worldRenderer) {
|
||||||
@@ -70,7 +70,7 @@ export default class SoundManager {
|
|||||||
} else if (pool.length > 0) {
|
} else if (pool.length > 0) {
|
||||||
// Play random sound in pool
|
// Play random sound in pool
|
||||||
let sound = pool[Math.floor(Math.random() * pool.length)];
|
let sound = pool[Math.floor(Math.random() * pool.length)];
|
||||||
if (typeof volume === "undefined") {
|
if (typeof volume === "undefined" || typeof sound === "undefined") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,10 @@ export default class EnumBlockFace {
|
|||||||
return this.isXAxis() ? 0.6 : this.isYAxis() ? 1.0 : 0.8;
|
return this.isXAxis() ? 0.6 : this.isYAxis() ? 1.0 : 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isPositive() {
|
||||||
|
return this.x > 0 || this.y > 0 || this.z > 0;
|
||||||
|
}
|
||||||
|
|
||||||
isXAxis() {
|
isXAxis() {
|
||||||
return this.x !== 0;
|
return this.x !== 0;
|
||||||
}
|
}
|
||||||
@@ -58,6 +62,25 @@ export default class EnumBlockFace {
|
|||||||
return this.x === other.x && this.y === other.y && this.z === other.z;
|
return this.x === other.x && this.y === other.y && this.z === other.z;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
switch (this) {
|
||||||
|
case EnumBlockFace.TOP:
|
||||||
|
return "top";
|
||||||
|
case EnumBlockFace.BOTTOM:
|
||||||
|
return "bottom";
|
||||||
|
case EnumBlockFace.NORTH:
|
||||||
|
return "north";
|
||||||
|
case EnumBlockFace.EAST:
|
||||||
|
return "east";
|
||||||
|
case EnumBlockFace.SOUTH:
|
||||||
|
return "south";
|
||||||
|
case EnumBlockFace.WEST:
|
||||||
|
return "west";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static values() {
|
static values() {
|
||||||
return [EnumBlockFace.TOP, EnumBlockFace.BOTTOM, EnumBlockFace.NORTH, EnumBlockFace.EAST, EnumBlockFace.SOUTH, EnumBlockFace.WEST];
|
return [EnumBlockFace.TOP, EnumBlockFace.BOTTOM, EnumBlockFace.NORTH, EnumBlockFace.EAST, EnumBlockFace.SOUTH, EnumBlockFace.WEST];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
export default class FocusStateType {
|
||||||
|
|
||||||
|
static REQUEST_EXIT = new FocusStateType(0, 1);
|
||||||
|
static EXITED = new FocusStateType(1, -1);
|
||||||
|
|
||||||
|
static REQUEST_LOCK = new FocusStateType(2, 3);
|
||||||
|
static LOCKED = new FocusStateType(3, -1);
|
||||||
|
|
||||||
|
constructor(id, intentId) {
|
||||||
|
this.id = id;
|
||||||
|
this.intentId = intentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
getIntent() {
|
||||||
|
return this.intentId === -1 ? null : FocusStateType.getById(this.intentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
isLock() {
|
||||||
|
return this === FocusStateType.REQUEST_LOCK || this === FocusStateType.LOCKED;
|
||||||
|
}
|
||||||
|
|
||||||
|
isRequest() {
|
||||||
|
return this === FocusStateType.REQUEST_LOCK || this === FocusStateType.REQUEST_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
isIntent() {
|
||||||
|
return !this.isRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
opposite() {
|
||||||
|
switch (this) {
|
||||||
|
case FocusStateType.REQUEST_EXIT:
|
||||||
|
return FocusStateType.REQUEST_LOCK;
|
||||||
|
case FocusStateType.REQUEST_LOCK:
|
||||||
|
return FocusStateType.REQUEST_EXIT;
|
||||||
|
case FocusStateType.EXITED:
|
||||||
|
return FocusStateType.LOCKED;
|
||||||
|
case FocusStateType.LOCKED:
|
||||||
|
return FocusStateType.EXITED;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getName() {
|
||||||
|
switch (this) {
|
||||||
|
case FocusStateType.REQUEST_EXIT:
|
||||||
|
return "REQUEST_EXIT";
|
||||||
|
case FocusStateType.REQUEST_LOCK:
|
||||||
|
return "REQUEST_LOCK";
|
||||||
|
case FocusStateType.EXITED:
|
||||||
|
return "EXITED";
|
||||||
|
case FocusStateType.LOCKED:
|
||||||
|
return "LOCKED";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static getById(id) {
|
||||||
|
switch (id) {
|
||||||
|
case 0:
|
||||||
|
return FocusStateType.REQUEST_EXIT;
|
||||||
|
case 1:
|
||||||
|
return FocusStateType.EXITED;
|
||||||
|
case 2:
|
||||||
|
return FocusStateType.REQUEST_LOCK;
|
||||||
|
case 3:
|
||||||
|
return FocusStateType.LOCKED;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
export default class Keyboard {
|
export default class Keyboard {
|
||||||
|
|
||||||
static state = {};
|
static state = {};
|
||||||
|
static enabled = false;
|
||||||
|
|
||||||
static create() {
|
static create() {
|
||||||
window.addEventListener('keydown', function (event) {
|
window.addEventListener('keydown', event => {
|
||||||
Keyboard.state[event.code] = true;
|
Keyboard.state[event.code] = true;
|
||||||
});
|
});
|
||||||
window.addEventListener('keyup', function (event) {
|
window.addEventListener('keyup', event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
delete Keyboard.state[event.code];
|
delete Keyboard.state[event.code];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Keyboard.setEnabled(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
static setState(key, state) {
|
static setState(key, state) {
|
||||||
@@ -21,7 +24,15 @@ export default class Keyboard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static isKeyDown(key) {
|
static isKeyDown(key) {
|
||||||
return Keyboard.state[key];
|
return Keyboard.state[key] && Keyboard.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
static setEnabled(enabled) {
|
||||||
|
Keyboard.enabled = enabled;
|
||||||
|
|
||||||
|
if (!enabled) {
|
||||||
|
Keyboard.unPressAll();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -99,4 +99,5 @@ export default class MathHelper {
|
|||||||
let h = c && ((v === r) ? (g - b) / c : ((v === g) ? 2 + (b - r) / c : 4 + (r - g) / c));
|
let h = c && ((v === r) ? (g - b) / c : ((v === g) ? 2 + (b - r) / c : 4 + (r - g) / c));
|
||||||
return [60 * (h < 0 ? h + 6 : h), v && c / v, v];
|
return [60 * (h < 0 ? h + 6 : h), v && c / v, v];
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user