Compare commits

..

36 Commits
format ... 1.20

Author SHA1 Message Date
470b5839a4 尝试修复客户端崩溃问题 2026-05-31 21:52:07 +08:00
GiantLuigi4
594c4decf3 improve keyboard camera 2024-10-21 23:53:07 -04:00
GiantLuigi4
ecb0dcee56 bump version, very start of audio stuff, fix a strange crash where camera is null 2024-10-19 00:53:48 -04:00
GiantLuigi4
cc18c6f793 improve handling of errors in the wd scheme
also config for join message
2024-10-18 00:11:58 -04:00
GiantLuigi4
6e712fc8da fix minisrv 2024-10-17 23:07:02 -04:00
GiantLuigi4
06e722cb7c screens can now be mined with a pickaxe 2024-10-17 12:36:11 -04:00
ds58
2ffe983e12
Merge pull request #17 from OtterCodes101/1.20
Add Modrinth link
2024-08-09 10:14:31 -05:00
OtterDev
5462ec7e93
Update README.md 2023-12-13 08:53:10 -07:00
GiantLuigi4
a603bdc8df - fix issues with minepad cursor lock
- keyboard now enables mouse controls
- fix some issues with the keyboard camera
- helper methods on the screen block entity for dealing with hit results
- update non-english language files to json
2023-11-29 13:54:42 -05:00
GiantLuigi4
21ffc08fcf keyboard camera mode config, fix some bugs with the ElementCenterQuery, fix ownership theif interacting with the wrong side 2023-11-27 18:05:18 -05:00
GiantLuigi4
cc0803de11 screen configurator now uses the correct direction 2023-11-27 15:24:43 -05:00
GiantLuigi4
7e7133e08c tweaks to js handling 2023-11-27 15:18:30 -05:00
GiantLuigi4
2e30446281 fix load distance 2023-11-27 11:18:12 -05:00
GiantLuigi4
9452482254 some refactoring 2023-11-27 10:35:26 -05:00
GiantLuigi4
91557161e0 pointer lock support 2023-11-26 00:46:53 -05:00
GiantLuigi4
85b076d638 ok I can push this too 2023-11-25 16:29:41 -05:00
GiantLuigi4
5292e97a8f start work on pointer lock api 2023-11-25 16:29:04 -05:00
GiantLuigi4
f843376f7a better mouse movement 2023-11-24 18:04:09 -05:00
GiantLuigi4
84d475d61c use a proper inverse of hit2px for the coordinate calculation for the keyboard camera 2023-11-22 23:59:18 -05:00
GiantLuigi4
f3f3cff079 not the best code quality, but it works how I wanted it to work 2023-11-22 22:52:08 -05:00
GiantLuigi4
fe81c18b5b I believe I have messed up... 2023-11-22 21:59:14 -05:00
GiantLuigi4
ec904c199e Merge remote-tracking branch 'origin/1.20' into 1.20
# Conflicts:
#	src/main/java/net/montoyo/wd/block/ScreenBlock.java
#	src/main/java/net/montoyo/wd/client/ClientProxy.java
#	src/main/java/net/montoyo/wd/client/gui/GuiKeyboard.java
#	src/main/java/net/montoyo/wd/client/gui/GuiScreenConfig.java
#	src/main/java/net/montoyo/wd/client/renderers/ScreenRenderer.java
#	src/main/java/net/montoyo/wd/controls/builtin/ManageRightsAndUpdgradesControl.java
#	src/main/java/net/montoyo/wd/data/ScreenConfigData.java
#	src/main/java/net/montoyo/wd/entity/AbstractInterfaceBlockEntity.java
#	src/main/java/net/montoyo/wd/entity/KeyboardBlockEntity.java
#	src/main/java/net/montoyo/wd/entity/RedstoneControlBlockEntity.java
#	src/main/java/net/montoyo/wd/entity/RemoteControlBlockEntity.java
#	src/main/java/net/montoyo/wd/entity/ScreenBlockEntity.java
#	src/main/java/net/montoyo/wd/item/ItemLaserPointer.java
#	src/main/java/net/montoyo/wd/item/ItemLinker.java
#	src/main/java/net/montoyo/wd/item/ItemOwnershipThief.java
#	src/main/java/net/montoyo/wd/item/ItemScreenConfigurator.java
#	src/main/java/net/montoyo/wd/net/client_bound/S2CMessageAddScreen.java
2023-11-22 21:54:43 -05:00
GiantLuigi4
281eb51c0d keyboard camera prototype 2023-11-22 21:48:54 -05:00
GiantLuigi4
3d2f786049 split "Screen" class out of TileEntityScreen 2023-11-22 15:51:38 -05:00
GiantLuigi4
3ca9f6ceb1
Merge pull request #7 from CinemaMod/format
Format
2023-11-11 18:05:22 -05:00
ds58
639fdefcce 2.0.1-1.20.1 2023-11-11 15:44:59 -06:00
ds58
1de20710fe Fix mcef dependency version in mods.toml 2023-11-11 15:44:22 -06:00
ds58
5b37a39dbc
Update README.md 2023-11-10 23:36:42 -06:00
ds58
0bc5b3b67d
Update README.md 2023-11-10 23:36:14 -06:00
ds58
dbd8fba683
Update README.md 2023-11-10 23:31:09 -06:00
ds58
d6da33da63 Merge branch '1.20' of https://github.com/CinemaMod/webdisplays into 1.20 2023-11-10 23:10:01 -06:00
ds58
2969470796 Update mods.toml 2023-11-10 23:09:32 -06:00
ds58
c2d6f01991
Add some images to README 2023-11-10 22:50:25 -06:00
ds58
96d7c92c37 MIT License, start new README, gradle.properties cleanup 2023-11-10 22:46:18 -06:00
ds58
6869461124 Update Specification-Title in MANIFEST 2023-11-10 21:55:38 -06:00
ds58
66212d2d16 Make gradlew executable, remove libs dir 2023-11-10 21:54:56 -06:00
58 changed files with 2567 additions and 1121 deletions

22
LICENSE
View File

@ -1 +1,21 @@
This mod and its source code is now in public domain. Feel free to do whatever you want with it; make forks, distribute it... whatever I would appreciate it, of course, if you credited me 😊 Thank you!
MIT License
Copyright (c) 2023 CinemaMod Group
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,10 +1,25 @@
# CinemaMod WebDisplays
This is a fork of the WebDisplays mod from 1.12, updated to work in newer versions, and with some bug fixes and reworking.
Cinemamod WebDisplays also uses CinemaMod's [MCEF](https://github.com/CinemaMod/mcef) and [JCEF](https://github.com/CinemaMod/java-cef), which should allow for WD to be a bit more seamless than [Chromium's JCEF](https://github.com/chromiumembedded/java-cef) would allow for.
# WebDisplays
WebDisplays adds a screen block, which allows you to browse the internet in minecraft.
WebDisplays is a mod for creating and interacting with web browsers in Minecraft. You can create screens in your world and browse the internet.
### Wiki
* A Wiki which details all blocks/items can be found on [montoyo's website](https://montoyo.net/wdwiki/). However, a lot of stuff has changed since that wiki was written.
WebDisplays was originally written by montoyo. It is currently maintained by CinemaMod Group.
Discussion: https://discord.gg/rNrh5kW8Ty
## Install
Download WebDisplays from either:
- CurseForge: https://legacy.curseforge.com/minecraft/mc-mods/webdisplays
- Modrinth: https://modrinth.com/mod/webdisplays
**WebDisplays Requires MCEF!** You must install MCEF in order for WebDisplays to work.
Download MCEF from either:
- CurseForge: https://legacy.curseforge.com/minecraft/mc-mods/mcef
- Modrinth: https://modrinth.com/mod/mcef
<img src='https://github.com/CinemaMod/webdisplays/assets/30220598/2acfd365-fa87-4adb-970a-33bb5c79f7ba' width='500'>
<img src='https://github.com/CinemaMod/webdisplays/assets/30220598/4e9985a3-d09f-4ab4-8016-37733d4f4a99' width='500'>
## Wiki
[Outdated Wiki from the original creator](https://montoyo.net/wdwiki/index.php?title=Main_Page)
[Outdated Getting Started](https://montoyo.net/wdwiki/index.php?title=Screen)

View File

@ -1,114 +1,179 @@
plugins {
id 'eclipse'
id 'java'
id 'idea'
id 'maven-publish'
id 'net.neoforged.gradle' version '[6.0.18,6.2)'
id 'org.spongepowered.mixin' version '0.7.+'
id 'org.parchmentmc.librarian.forgegradle' version '1.+'
id 'com.github.johnrengelman.shadow' version '8.1.1'
id 'net.neoforged.moddev.legacyforge' version '2.0.103'
}
apply plugin: 'org.spongepowered.mixin'
println('Java: ' + System.getProperty('java.version') + ' JVM: ' + System.getProperty('java.vm.version') + '(' + System.getProperty('java.vendor') + ') Arch: ' + System.getProperty('os.arch'))
// Only add ProGuard if building with it
def enableProguard = project.hasProperty("enableProguard") && project.enableProguard.toBoolean()
if (enableProguard) {
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.guardsquare:proguard-gradle:7.7.0'
}
}
}
version = mod_version
group = maven_group // http://maven.apache.org/guides/mini/guide-naming-conventions.html
archivesBaseName = archives_base_name
base {
archivesName = mod_id
}
group = mod_group_id
version = "${minecraft_version}-${mod_version}"
// set java version
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
// required for making a functional mod
sourceSets.main.resources.srcDirs += 'src/generated/resources'
mixin.add sourceSets.main, "webdisplays.refmap.json"
println "Java: ${System.getProperty 'java.version'}"
// neoforge gradle
minecraft {
mappings channel: 'parchment', version: '2023.06.26-1.20.1'
repositories {
mavenCentral()
maven { url = "https://libraries.minecraft.net/" }
maven { url = "https://cursemaven.com" }
maven { url = "https://maven.parchmentmc.org" }
maven { url = 'https://mcef-download.cinemamod.com/repositories/releases/' }
maven { url = "https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/" }
maven { url = "https://maven.theillusivec4.top/" }
maven { url = "https://repo.lucko.me/" }
maven { url = "https://maven.kosmx.dev/" }
maven { url = "https://modmaven.dev" }
flatDir { dir "libs" }
}
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
legacyForge {
version = "${minecraft_version}-${forge_version}"
copyIdeResources = true
// Access transformer
accessTransformers = files("src/main/resources/META-INF/accesstransformer.cfg")
// Parchment mappings
parchment {
minecraftVersion = minecraft_version
mappingsVersion = mapping_lasting_version
}
runs {
client {
properties 'mixin.env.remapRefMap': 'true'
property 'mixin.env.refMapRemappingFile', "${project.projectDir}/build/createSrgToMcp/output.srg"
workingDirectory project.file('run')
arg "-mixin.config=webdisplays.mixins.json"
property 'forge.logging.console.level', 'debug'
configureEach {
systemProperty 'forge.logging.console.level', 'debug'
logLevel = org.slf4j.event.Level.DEBUG
}
mods {
webdisplays {
source sourceSets.main
}
}
client {
client()
programArgument "-mixin.config=${mod_id}.mixins.json"
systemProperty 'mixin.debug.export', 'true'
}
server {
properties 'mixin.env.remapRefMap': 'true'
property 'mixin.env.refMapRemappingFile', "${project.projectDir}/build/createSrgToMcp/output.srg"
workingDirectory project.file('run')
arg "-mixin.config=webdisplays.mixins.json"
property 'forge.logging.console.level', 'debug'
mods {
webdisplays {
source sourceSets.main
}
}
server()
programArgument "-mixin.config=${mod_id}.mixins.json"
}
data {
workingDirectory project.file('run')
properties 'mixin.env.remapRefMap': 'true'
property 'mixin.env.refMapRemappingFile', "${project.projectDir}/build/createSrgToMcp/output.srg"
property 'forge.logging.console.level', 'debug'
args '--mod', 'webdisplays', '--all', '--output', file('src/generated/resources/'), '--existing', sourceSets.main.resources.srcDirs[0]
mods {
webdisplays {
source sourceSets.main
}
}
data()
programArguments.addAll '--mod', mod_id, '--all',
'--output', file('src/generated/resources/').absolutePath,
'--existing', file('src/main/resources/').absolutePath
}
}
mods {
"${mod_id}" {
sourceSet sourceSets.main
}
}
}
repositories{
maven {
name = "cursemaven"
url = "https://www.cursemaven.com"
}
flatDir { dirs 'libs' }
maven { url 'https://mcef-download.cinemamod.com/repositories/releases/' }
sourceSets.main.resources {
srcDir 'src/generated/resources'
}
dependencies {
minecraft 'net.neoforged:forge:1.20.1-47.1.65'
annotationProcessor 'org.spongepowered:mixin:0.8.5:processor'
// useful for debugging performance problems
implementation fg.deobf("curse.maven:spark-361579:4381167")
// here because we need to manually open the VR keyboard
compileOnly fg.deobf("curse.maven:vivecraft-667903:4794431")
implementation fg.deobf("com.cinemamod:mcef-forge:2.1.1-1.20.1") {
// MCEF dependency
modImplementation("com.cinemamod:mcef-forge:2.1.1-1.20.1") {
transitive = false
}
// Optional mods (make sure versions exist)
modRuntimeOnly "curse.maven:spark-361579:4738952"
modCompileOnly "curse.maven:vivecraft-667903:4794431"
modImplementation "software.bernie.geckolib:geckolib-forge-${minecraft_version}:${geckolib_version}"
modCompileOnly "top.theillusivec4.curios:curios-forge:${curios_version}:api"
modRuntimeOnly "top.theillusivec4.curios:curios-forge:${curios_version}"
// Mixin Extras
compileOnly annotationProcessor("io.github.llamalad7:mixinextras-common:0.4.1")
modImplementation "io.github.llamalad7:mixinextras-forge:0.4.1"
}
// jar meta-info
jar {
tasks.named('jar', Jar) {
manifest {
attributes([
"Specification-Title": "Webdisplays",
"Specification-Vendor": "CinemaMod Group",
"Specification-Version": "1", // We are version 1 of ourselves
"Implementation-Title": project.name,
"Implementation-Version": project.version,
"Implementation-Vendor": "CinemaMod Group",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
"MixinConfigs": "webdisplays.mixins.json"
'Specification-Title': mod_name,
'Specification-Vendor': mod_authors,
'Specification-Version': '1',
'Implementation-Title': project.name,
'Implementation-Version': project.version,
'Implementation-Vendor': mod_authors,
'MixinConfigs': "${mod_id}.mixins.json"
])
}
// Exclude ProGuard renamed files if ProGuard is used
if (enableProguard) {
exclude 'META-INF/versions/**'
}
}
// Generate sources jar
tasks.register('sourcesJar', Jar) {
dependsOn classes
archiveClassifier = 'sources'
from sourceSets.main.allSource
}
// Reobfuscation handling
tasks.named('reobfJar') {
if (enableProguard) {
dependsOn 'proguard'
input = file("${buildDir}/libs/${mod_id}-${minecraft_version}-${mod_version}-proguard.jar")
} else {
dependsOn 'jar'
input = tasks.jar.archiveFile.get().asFile
}
}
// ProGuard configuration (if enabled)
if (enableProguard) {
tasks.register('proguard', proguard.gradle.ProGuardTask) {
dependsOn tasks.jar
configuration 'proguard.pro'
libraryjars "${System.getProperty('java.home')}/jmods"
def inputJar = tasks.jar.archiveFile.get().asFile
injars inputJar
outjars "${buildDir}/libs/${mod_id}-${minecraft_version}-${mod_version}-proguard.jar"
}
}
publishing {
publications {
mavenJava(MavenPublication) {
artifactId = mod_id
artifact reobfJar
artifact sourcesJar
}
}
repositories {
maven {
name = "local"
url = layout.buildDirectory.dir("repo")
}
}
}

View File

@ -1,17 +1,36 @@
# Done to increase the memory available to gradle.
# Gradle settings
org.gradle.jvmargs=-Xmx3G
loom.platform=forge
org.gradle.daemon=false
org.gradle.parallel=true
org.gradle.caching=true
# Fabric Properties
# check these on https://fabricmc.net/develop
minecraft_version=1.19.2
yarn_mappings=1.19.2+build.28
loader_version=0.14.14
# ProGuard
enableProguard=false
# Mod Properties
mod_version = 1.3.3
maven_group = net.montoyo.wd
archives_base_name = webdisplays
# Mod properties
mod_id=webdisplays
mod_name=WebDisplays
mod_version=2.0.3
mod_group_id=com.cinemamod
mod_authors=CinemaMod Group
mod_description=Web browser displays for Minecraft
mod_license=All Rights Reserved
mod_credits=CinemaMod Group
# Dependencies
forge_version=1.19.2-43.2.6
# Minecraft/Forge versions
minecraft_version=1.20.1
forge_version=47.3.4
minecraft_version_range=[1.20.1,1.21)
forge_version_range=[47,)
loader_version_range=[47,)
# Parchment mappings
mapping_channel=parchment
mapping_version=2023.09.03-1.20.1
mapping_lasting_version=2023.09.03
# Other mod versions
jei_version=15.20.0.112
player_anim_version=1.0.2-rc1+1.20
geckolib_version=4.2.1
curios_version=5.5.0+1.20.1

0
gradlew vendored Normal file → Executable file
View File

Binary file not shown.

View File

@ -1,28 +1,11 @@
pluginManagement {
repositories {
mavenLocal()
gradlePluginPortal()
maven {
name = 'NeoForged'
url = 'https://maven.neoforged.net/releases'
}
maven { url = 'https://maven.neoforged.net/releases' }
maven { url = 'https://maven.parchmentmc.org' } // Add this line
}
// resolutionStrategy {
// eachPlugin {
// switch (requested.id.toString()) {
// case "net.minecraftforge.gradle": {
// useModule("${requested.id}:ForgeGradle:${requested.version}")
// break
// }
// case "org.spongepowered.mixin": {
// useModule("org.spongepowered:mixingradle:${requested.version}")
// break;
// }
// }
// }
// }
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0'
}
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}

View File

@ -14,18 +14,21 @@ import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.server.ServerLifecycleHooks;
import net.montoyo.wd.core.HasAdvancement;
import net.montoyo.wd.core.JSServerRequest;
import net.montoyo.wd.data.GuiData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.utilities.*;
import net.montoyo.wd.utilities.math.Vector2i;
import net.montoyo.wd.utilities.math.Vector3i;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.data.Rotation;
import net.montoyo.wd.utilities.serialization.NameUUIDPair;
import org.joml.Vector3d;
import javax.annotation.Nonnull;
import java.util.UUID;
@ -41,7 +44,7 @@ public class SharedProxy {
public void postInit() {
}
public void onCefInit(/*CefInitEvent event*/) {
public void onCefInit() {
}
@Deprecated(forRemoval = true)
@ -125,4 +128,21 @@ public class SharedProxy {
public boolean isShiftDown() {
return false;
}
public double distanceTo(ScreenBlockEntity tes, Vec3 position) {
double dist = Double.POSITIVE_INFINITY;
for (int i = 0; i < tes.screenCount(); i++) {
ScreenData scrn = tes.getScreen(i);
Vector3d pos = new Vector3d(
scrn.side.right.x * scrn.size.x / 2d + scrn.size.y * scrn.side.up.x / 2d,
scrn.side.right.y * scrn.size.x / 2d + scrn.size.y * scrn.side.up.y / 2d,
scrn.side.right.z * scrn.size.x / 2d + scrn.size.y * scrn.side.up.z / 2d
).add(tes.getBlockPos().getX(), tes.getBlockPos().getY(), tes.getBlockPos().getZ());
double dist2 = position.distanceToSqr(pos.x, pos.y, pos.z);
dist = Math.min(dist, dist2);
}
return dist;
}
}

View File

@ -38,17 +38,18 @@ import net.minecraftforge.network.PacketDistributor;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.montoyo.wd.client.ClientProxy;
import net.montoyo.wd.client.gui.camera.KeyboardCamera;
import net.montoyo.wd.config.ClientConfig;
import net.montoyo.wd.config.CommonConfig;
import net.montoyo.wd.controls.ScreenControlRegistry;
import net.montoyo.wd.core.*;
import net.montoyo.wd.registry.BlockRegistry;
import net.montoyo.wd.registry.ItemRegistry;
import net.montoyo.wd.registry.WDTabs;
import net.montoyo.wd.registry.TileRegistry;
import net.montoyo.wd.miniserv.server.Server;
import net.montoyo.wd.net.WDNetworkRegistry;
import net.montoyo.wd.net.client_bound.S2CMessageServerInfo;
import net.montoyo.wd.registry.BlockRegistry;
import net.montoyo.wd.registry.ItemRegistry;
import net.montoyo.wd.registry.TileRegistry;
import net.montoyo.wd.registry.WDTabs;
import net.montoyo.wd.utilities.DistSafety;
import net.montoyo.wd.utilities.Log;
import net.montoyo.wd.utilities.serialization.Util;
@ -114,6 +115,8 @@ public class WebDisplays {
// proxies are annoying, so from now on, I'mma be just registering stuff in here
FMLJavaModLoadingContext.get().getModEventBus().addListener(ClientProxy::onKeybindRegistry);
MinecraftForge.EVENT_BUS.addListener(ClientProxy::onDrawSelection);
MinecraftForge.EVENT_BUS.addListener(KeyboardCamera::updateCamera);
MinecraftForge.EVENT_BUS.addListener(KeyboardCamera::gameTick);
ClientConfig.init();
}
@ -288,6 +291,10 @@ public class WebDisplays {
@SubscribeEvent
public void onLogIn(PlayerEvent.PlayerLoggedInEvent ev) {
if (!CommonConfig.joinMessage) {
return;
}
if(!ev.getEntity().level().isClientSide && ev.getEntity() instanceof ServerPlayer) {
IWDDCapability cap = ev.getEntity().getCapability(WDDCapability.Provider.cap, null).orElseThrow(RuntimeException::new);

View File

@ -32,6 +32,7 @@ import net.montoyo.wd.core.DefaultUpgrade;
import net.montoyo.wd.core.IUpgrade;
import net.montoyo.wd.core.ScreenRights;
import net.montoyo.wd.data.SetURLData;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.item.ItemLaserPointer;
import net.montoyo.wd.utilities.*;
@ -100,7 +101,7 @@ public class ScreenBlock extends BaseEntityBlock {
ScreenBlockEntity te = (ScreenBlockEntity) world.getBlockEntity(pos.toBlock());
if (te != null && te.getScreen(side) != null) {
ScreenBlockEntity.Screen scr = te.getScreen(side);
ScreenData scr = te.getScreen(side);
if (sneaking) { //Right Click
if ((scr.rightsFor(player) & ScreenRights.CHANGE_URL) == 0)
@ -198,8 +199,8 @@ public class ScreenBlock extends BaseEntityBlock {
}
}
}
public static boolean hit2pixels(BlockSide side, BlockPos bpos, Vector3i pos, ScreenBlockEntity.Screen scr, float hitX, float hitY, float hitZ, Vector2i dst) {
public static boolean hit2pixels(BlockSide side, BlockPos bpos, Vector3i pos, ScreenData scr, float hitX, float hitY, float hitZ, Vector2i dst) {
if(side.right.x < 0)
hitX -= 1.f;
@ -208,6 +209,7 @@ public class ScreenBlock extends BaseEntityBlock {
Vector3f rel = new Vector3f(hitX, hitY, hitZ);
// this dot is acting as a "get distance from plane" where the plane is the edge of the screen
float cx = rel.dot(side.right.toFloat()) - 2.f / 16.f;
float cy = rel.dot(side.up.toFloat()) - 2.f / 16.f;
float sw = ((float) scr.size.x) - 4.f / 16.f;

View File

@ -12,6 +12,7 @@ import com.mojang.blaze3d.platform.InputConstants;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementProgress;
import net.minecraft.client.Camera;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Options;
@ -19,9 +20,8 @@ import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen;
import net.minecraft.client.multiplayer.ClientAdvancements;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderers;
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
@ -32,6 +32,7 @@ import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceManagerReloadListener;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.Slot;
@ -40,14 +41,16 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.*;
import net.minecraftforge.client.event.ModelEvent;
import net.minecraftforge.client.event.RegisterKeyMappingsEvent;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.client.event.RenderHighlightEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.level.LevelEvent;
@ -65,27 +68,27 @@ import net.montoyo.wd.client.renderers.*;
import net.montoyo.wd.core.HasAdvancement;
import net.montoyo.wd.data.GuiData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.registry.BlockRegistry;
import net.montoyo.wd.registry.ItemRegistry;
import net.montoyo.wd.registry.TileRegistry;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.item.ItemLaserPointer;
import net.montoyo.wd.item.ItemMinePad2;
import net.montoyo.wd.item.WDItem;
import net.montoyo.wd.miniserv.client.Client;
import net.montoyo.wd.net.WDNetworkRegistry;
import net.montoyo.wd.net.server_bound.C2SMessageMinepadUrl;
import net.montoyo.wd.utilities.*;
import net.montoyo.wd.utilities.math.Vector2i;
import net.montoyo.wd.utilities.math.Vector3i;
import net.montoyo.wd.registry.BlockRegistry;
import net.montoyo.wd.registry.ItemRegistry;
import net.montoyo.wd.registry.TileRegistry;
import net.montoyo.wd.utilities.Log;
import net.montoyo.wd.utilities.Multiblock;
import net.montoyo.wd.utilities.browser.WDBrowser;
import net.montoyo.wd.utilities.browser.handlers.DisplayHandler;
import net.montoyo.wd.utilities.browser.handlers.WDRouter;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.data.Rotation;
import net.montoyo.wd.utilities.math.Vector2i;
import net.montoyo.wd.utilities.math.Vector3i;
import net.montoyo.wd.utilities.serialization.NameUUIDPair;
import org.cef.CefSettings;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.handler.CefDisplayHandler;
import org.cef.browser.CefMessageRouter;
import org.cef.misc.CefCursorType;
import org.joml.Vector3d;
import org.lwjgl.glfw.GLFW;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@ -95,10 +98,9 @@ import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.*;
import java.util.stream.Stream;
@Mod.EventBusSubscriber(modid = "webdisplays", value = Dist.CLIENT, bus = Mod.EventBusSubscriber.Bus.MOD)
public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJSQueryHandler*/, ResourceManagerReloadListener {
public class ClientProxy extends SharedProxy implements ResourceManagerReloadListener {
private static ClientProxy INSTANCE;
@ -163,8 +165,8 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
Multiblock.findOrigin(mc.level, pos, side, null);
ScreenBlockEntity te = (ScreenBlockEntity) mc.level.getBlockEntity(pos.toBlock());
ScreenBlockEntity.Screen sc = te.getScreen(side);
ScreenData sc = te.getScreen(side);
if (sc == null) return;
@ -183,12 +185,20 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
ci.cancel();
}
public List<ScreenBlockEntity> getScreens() {
return screenTracking;
}
public List<PadData> getPads() {
return padList;
}
public class PadData {
public CefBrowser view;
public final UUID id;
private boolean isInHotbar;
private final UUID id;
private long lastURLSent;
public int activeCursor;
@ -200,7 +210,7 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
} catch (IOException e) {
throw new RuntimeException(e);
}
view = MCEF.createBrowser(WebDisplays.applyBlacklist(webUrl), false);
view = WDBrowser.createBrowser(WebDisplays.applyBlacklist(webUrl), false);
if (view instanceof MCEFBrowser browser) {
browser.resize((int) WebDisplays.INSTANCE.padResX, (int) WebDisplays.INSTANCE.padResY);
browser.setCursorChangeListener((cursor) -> {
@ -210,11 +220,18 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
isInHotbar = true;
this.id = id;
}
public void updateTime() {
lastURLSent = System.currentTimeMillis();
}
public long lastSent() {
return lastURLSent;
}
}
private Minecraft mc;
private MinePadRenderer minePadRenderer;
// private JSQueryDispatcher jsDispatcher;
private LaserPointerRenderer laserPointerRenderer;
private Screen nextScreen;
private boolean isF1Down;
@ -248,10 +265,6 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
event.register(ScreenModelLoader.SCREEN_LOADER.getPath(), new ScreenModelLoader());
}
private static void registerBlockRenderLayers(RenderType layer, Block... blocks) {
Stream.of(blocks).forEach(block -> ItemBlockRenderTypes.setRenderLayer(block, layer));
}
@Override
public void preInit() {
super.preInit();
@ -260,12 +273,7 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
}
@Override
public void init() {
super.init();
}
@Override
public void onCefInit(/*CefInitEvent event*/) {
public void onCefInit() {
minePadRenderer = new MinePadRenderer();
laserPointerRenderer = new LaserPointerRenderer();
@ -279,9 +287,8 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
}
);
// jsDispatcher = new JSQueryDispatcher(this);
MCEF.getClient().addDisplayHandler(this);
// mcef.registerJSQueryHandler(this);
MCEF.getClient().addDisplayHandler(DisplayHandler.INSTANCE);
MCEF.getClient().getHandle().addMessageRouter(CefMessageRouter.create(WDRouter.INSTANCE));
findAdvancementToProgressField();
}
@ -518,55 +525,6 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
GuiLoader.clearCache();
}
/**************************************** DISPLAY HANDLER METHODS ****************************************/
@Override
public void onAddressChange(CefBrowser browser, CefFrame cefFrame, String url) {
if (browser != null) {
long t = System.currentTimeMillis();
for (PadData pd : padList) {
if (pd.view == browser && t - pd.lastURLSent >= 1000) {
if (WebDisplays.isSiteBlacklisted(url))
pd.view.loadURL(WebDisplays.BLACKLIST_URL);
else {
pd.lastURLSent = t; //Avoid spamming the server with porn URLs
WDNetworkRegistry.INSTANCE.sendToServer(new C2SMessageMinepadUrl(pd.id, url));
}
break;
}
}
for (ScreenBlockEntity tes : screenTracking)
tes.updateClientSideURL(browser, url);
}
}
@Override
public void onTitleChange(CefBrowser cefBrowser, String s) {
}
@Override
public boolean onTooltip(CefBrowser cefBrowser, String s) {
return false;
}
@Override
public void onStatusMessage(CefBrowser cefBrowser, String s) {
}
@Override
public boolean onConsoleMessage(CefBrowser cefBrowser, CefSettings.LogSeverity logSeverity, String s, String s1, int i) {
return false;
}
@Override
public boolean onCursorChange(CefBrowser cefBrowser, int i) {
return false;
}
/**************************************** JS HANDLER METHODS ****************************************/
// @Override
@ -606,45 +564,16 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
// @Override
// public void cancelQuery(IBrowser browser, long queryId) {
// }
/**************************************** EVENT METHODS ****************************************/
// @SubscribeEvent
// public void onBakeModel(ModelBakeEvent ev) {
// for(ResourceModelPair pair : modelBakers)
// ev.getModelRegistry().put(pair.getResourceLocation(), pair.getModel());
// }
/* @SubscribeEvent
public void onRegisterModels(ModelRegistryEvent ev) {
final WebDisplays wd = WebDisplays.INSTANCE;
//I hope I'm doing this right because it doesn't seem like it...
registerItemModel(wd.blockScreen.getItem(), 0, "inventory");
ModelLoaderRegistry.setCustomModelResourceLocation(wd.blockPeripheral.getItem(), 0, new ModelResourceLocation("webdisplays:kb_inv", "normal"));
registerItemModel(wd.blockPeripheral.getItem(), 1, "facing=2,type=ccinterface");
registerItemModel(wd.blockPeripheral.getItem(), 2, "facing=2,type=cointerface");
registerItemModel(wd.blockPeripheral.getItem(), 3, "facing=0,type=remotectrl");
registerItemModel(wd.blockPeripheral.getItem(), 7, "facing=0,type=redstonectrl");
registerItemModel(wd.blockPeripheral.getItem(), 11, "facing=0,type=server");
registerItemModel(wd.itemScreenCfg, 0, "normal");
registerItemModel(wd.itemOwnerThief, 0, "normal");
registerItemModel(wd.itemLinker, 0, "normal");
registerItemModel(wd.itemMinePad, 0, "normal");
registerItemModel(wd.itemMinePad, 1, "normal");
registerItemModel(wd.itemLaserPointer, 0, "normal");
registerItemMultiModels(wd.itemUpgrade);
registerItemMultiModels(wd.itemCraftComp);
registerItemMultiModels(wd.itemAdvIcon);
} */
@SubscribeEvent
public void onLevelTick(TickEvent.LevelTickEvent ev) {
if (!ev.side.equals(LogicalSide.CLIENT)) return;
if (ev.phase != TickEvent.Phase.END) return;
//Unload/load screens depending on client player distance
if (mc.player != null || !screenTracking.isEmpty())
if (mc.player == null || screenTracking.isEmpty())
return;
int id = lastTracked % screenTracking.size();
@ -661,27 +590,31 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
if (!tes.isLoaded())
tes.load();
} else {
double dist = Double.POSITIVE_INFINITY;
for (int i = 0; i < tes.screenCount(); i++) {
ScreenBlockEntity.Screen scrn = tes.getScreen(i);
Vector3d pos = new Vector3d(
scrn.side.right.x * scrn.size.x + scrn.size.y * scrn.side.up.x,
scrn.side.right.y * scrn.size.x + scrn.size.y * scrn.side.up.y,
scrn.side.right.z * scrn.size.x + scrn.size.y * scrn.side.up.z
);
double dist2 = mc.player.distanceToSqr(pos.x, pos.y, pos.z);
dist = Math.min(dist, dist2);
EntityRenderDispatcher entityRenderDispatcher = mc.getEntityRenderDispatcher();
if (entityRenderDispatcher == null) return;
Camera camera = entityRenderDispatcher.camera;
Entity entity = null;
// ide inspection says this is a bunch of constant expressions
// THIS IS NOT THE CASE
// a crash HAS occurred because of this going unchecked, and I'm confused about it
//noinspection ConstantValue
if (camera != null) entity = camera.getEntity();
//noinspection ConstantValue
if (entity == null) entity = mc.player;
//noinspection ConstantValue
if (entity != null) {
double dist = distanceTo(tes, entity.getPosition(0));
if (tes.isLoaded()) {
if (dist > WebDisplays.INSTANCE.unloadDistance2 * 16)
tes.deactivate();
// else if (ClientConfig.AutoVolumeControl.enableAutoVolume)
// tes.updateTrackDistance(dist, 80); //ToDo find master volume
} else if (dist <= WebDisplays.INSTANCE.loadDistance2 * 16)
tes.activate();
}
if (tes.isLoaded()) {
if (dist > WebDisplays.INSTANCE.unloadDistance2)
tes.unload();
// else if (ClientConfig.AutoVolumeControl.enableAutoVolume)
// tes.updateTrackDistance(dist, 80); //ToDo find master volume
} else if (dist <= WebDisplays.INSTANCE.loadDistance2)
tes.load();
}
}
@ -762,9 +695,6 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
} else {
ItemLaserPointer.deselect(mc);
}
// //Handle JS queries
// jsDispatcher.handleQueries();
//Miniserv
if (msClientStarted && mc.player == null) {
@ -862,7 +792,7 @@ public class ClientProxy extends SharedProxy implements CefDisplayHandler/*, IJS
public boolean findScreenFromBrowser(CefBrowser browser, ScreenSidePair pair) {
for (ScreenBlockEntity tes : screenTracking) {
for (int i = 0; i < tes.screenCount(); i++) {
ScreenBlockEntity.Screen scr = tes.getScreen(i);
ScreenData scr = tes.getScreen(i);
if (scr.browser == browser) {
pair.tes = tes;

View File

@ -16,8 +16,8 @@ import org.cef.misc.StringRef;
import org.cef.network.CefRequest;
import org.cef.network.CefResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
public class WDScheme implements CefResourceHandler {
@ -27,6 +27,7 @@ public class WDScheme implements CefResourceHandler {
private boolean isErrorPage;
String url;
boolean onlyError = false;
public WDScheme(String url) {
this.url = url;
@ -36,79 +37,94 @@ public class WDScheme implements CefResourceHandler {
public boolean processRequest(CefRequest cefRequest, CefCallback cefCallback) {
url = cefRequest.getURL();
url = url.substring("webdisplays://".length());
int pos = url.indexOf('/');
if(pos < 0)
if (pos < 0)
return false;
String uuidStr = url.substring(0, pos);
String fileStr = url.substring(pos + 1);
try {
fileStr = URLDecoder.decode(fileStr, "UTF-8");
} catch(UnsupportedEncodingException ex) {
Log.warningEx("UTF-8 isn't supported... yeah... and I'm a billionaire...", ex);
}
fileStr = URLDecoder.decode(fileStr, StandardCharsets.UTF_8);
if(uuidStr.isEmpty() || Util.isFileNameInvalid(fileStr))
return false;
if (uuidStr.isEmpty() || Util.isFileNameInvalid(fileStr)) {
// invalid URL or no UUID
onlyError = true;
cefCallback.Continue();
return true;
}
UUID uuid;
try {
uuid = UUID.fromString(uuidStr);
} catch(IllegalArgumentException ex) {
return false; //Invalid UUID
} catch (IllegalArgumentException ex) {
// invalid UUID
onlyError = true;
cefCallback.Continue();
return true;
}
task = new ClientTaskGetFile(uuid, fileStr);
return Client.getInstance().addTask(task) ? true : false;
boolean doContinue = Client.getInstance().addTask(task);
if (doContinue) cefCallback.Continue();
return doContinue;
}
@Override
public void getResponseHeaders(CefResponse cefResponse, IntRef intRef, StringRef stringRef) {
Log.info("Waiting for response...");
int status = task.waitForResponse();
Log.info("Got response %d", status);
public void getResponseHeaders(CefResponse cefResponse, IntRef contentLength, StringRef redir) {
int status;
if (onlyError) {
status = Constants.GETF_STATUS_BAD_NAME;
} else {
Log.info("Waiting for response...");
status = task.waitForResponse();
Log.info("Got response %d", status);
if(status == 0) {
//OK
int extPos = task.getFileName().lastIndexOf('.');
if(extPos >= 0) {
String mime = mapMime(task.getFileName().substring(extPos + 1));
if (status == 0) {
//OK
int extPos = task.getFileName().lastIndexOf('.');
if (extPos >= 0) {
String mime = mapMime(task.getFileName().substring(extPos + 1));
if(mime != null)
cefResponse.setMimeType(mime);
if (mime != null)
cefResponse.setMimeType(mime);
}
cefResponse.setStatus(200);
cefResponse.setStatusText("OK");
contentLength.set(0);
return;
}
cefResponse.setStatus(200);
cefResponse.setStatusText("OK");
cefResponse.setHeaderByName("content-length", "" + -1, true);
return;
}
int errCode;
String errStr;
if(status == Constants.GETF_STATUS_NOT_FOUND) {
if (status == Constants.GETF_STATUS_NOT_FOUND) {
errCode = 404;
errStr = "Not Found";
} else if (status == Constants.GETF_STATUS_TIMED_OUT) {
errCode = 408;
errStr = "Timed Out";
} else if (status == Constants.GETF_STATUS_BAD_NAME) {
errCode = 418;
errStr = "I'm a teapot";
} else {
errCode = 500;
errStr = "Internal Server Error";
}
cefResponse.setStatus(errCode);
cefResponse.setStatusText(errStr);
// reporting the actual status and text makes CEF not display the page
cefResponse.setStatus(200);
cefResponse.setStatusText("OK");
cefResponse.setMimeType("text/html");
try {
dataToWrite = String.format(ERROR_PAGE, errCode, errStr).getBytes("UTF-8");
dataOffset = 0;
amountToWrite = dataToWrite.length;
isErrorPage = true;
cefResponse.setHeaderByName("content-length", "" + amountToWrite, true);
} catch(UnsupportedEncodingException ex) {
cefResponse.setHeaderByName("content-length", "" + 0, true);
// cefResponse.setResponseLength(0);
}
dataToWrite = String.format(ERROR_PAGE, errCode, errStr).getBytes(StandardCharsets.UTF_8);
dataOffset = 0;
amountToWrite = dataToWrite.length;
isErrorPage = true;
contentLength.set(0);
}
private byte[] dataToWrite;
@ -116,10 +132,10 @@ public class WDScheme implements CefResourceHandler {
private int amountToWrite;
@Override
public boolean readResponse(byte[] bytes, int i, IntRef intRef, CefCallback cefCallback) {
if(dataToWrite == null) {
if(isErrorPage) {
// data.setAmountRead(0);
public boolean readResponse(byte[] output, int bytesToRead, IntRef bytesRead, CefCallback cefCallback) {
if (dataToWrite == null) {
if (isErrorPage) {
bytesRead.set(0);
return false;
}
@ -127,25 +143,25 @@ public class WDScheme implements CefResourceHandler {
dataOffset = 3; //packet ID + size
amountToWrite = task.getDataLength();
if(amountToWrite <= 0) {
if (amountToWrite <= 0) {
dataToWrite = null;
// data.setAmountRead(0);
bytesRead.set(0);
return false;
}
}
// int toWrite = data.getBytesToRead();
// if(toWrite > amountToWrite)
// toWrite = amountToWrite;
int toWrite = bytesToRead;
if (toWrite > amountToWrite)
toWrite = amountToWrite;
// System.arraycopy(dataToWrite, dataOffset, data.getDataArray(), 0, toWrite);
// data.setAmountRead(toWrite);
System.arraycopy(dataToWrite, dataOffset, output, 0, toWrite);
bytesRead.set(toWrite);
// dataOffset += toWrite;
// amountToWrite -= toWrite;
dataOffset += toWrite;
amountToWrite -= toWrite;
if(amountToWrite <= 0) {
if(!isErrorPage)
if (amountToWrite <= 0) {
if (!isErrorPage)
task.nextData();
dataToWrite = null;
@ -156,6 +172,9 @@ public class WDScheme implements CefResourceHandler {
@Override
public void cancel() {
Log.info("Scheme query canceled or finished.");
if (!onlyError)
task.cancel();
}
public static String mapMime(String ext) {

View File

@ -0,0 +1,116 @@
package net.montoyo.wd.client.audio;
import net.minecraft.client.resources.sounds.Sound;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.sounds.AudioStream;
import net.minecraft.client.sounds.SoundBufferLibrary;
import net.minecraft.client.sounds.SoundManager;
import net.minecraft.client.sounds.WeighedSoundEvents;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.util.valueproviders.SampledFloat;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.entity.ScreenData;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public class WDAudioSource implements SoundInstance {
private static final ResourceLocation location = new ResourceLocation("webdisplays:audio_source");
private static final WeighedSoundEvents events = new WeighedSoundEvents(
location, "webdisplays.browser"
);
private static final SampledFloat CONST_1 = new SampledFloat() {
@Override
public float sample(RandomSource pRandom) {
return 1.0f;
}
};
private final Sound sound = new Sound(
"unused",
CONST_1,
CONST_1,
1, Sound.Type.SOUND_EVENT,
true, false,
100
);
ScreenBlockEntity blockEntity;
ScreenData data;
public WDAudioSource(ScreenBlockEntity blockEntity, ScreenData data) {
this.blockEntity = blockEntity;
this.data = data;
}
@Override
public ResourceLocation getLocation() {
return location;
}
@Nullable
@Override
public WeighedSoundEvents resolve(SoundManager pManager) {
return events;
}
@Override
public CompletableFuture<AudioStream> getStream(SoundBufferLibrary soundBuffers, Sound sound, boolean looping) {
return null;
}
@Override
public Sound getSound() {
return sound;
}
@Override
public SoundSource getSource() {
return SoundSource.RECORDS;
}
@Override
public boolean isLooping() {
return true;
}
@Override
public boolean isRelative() {
return false;
}
@Override
public int getDelay() {
return 0;
}
@Override
public float getVolume() {
return blockEntity.ytVolume;
}
@Override
public float getPitch() {
return 1;
}
@Override
public double getX() {
return blockEntity.getBlockPos().getX();
}
@Override
public double getY() {
return blockEntity.getBlockPos().getY();
}
@Override
public double getZ() {
return blockEntity.getBlockPos().getZ();
}
@Override
public Attenuation getAttenuation() {
return Attenuation.LINEAR;
}
}

View File

@ -4,36 +4,50 @@
package net.montoyo.wd.client.gui;
import com.cinemamod.mcef.MCEFBrowser;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.loading.FMLPaths;
import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.client.gui.camera.KeyboardCamera;
import net.montoyo.wd.client.gui.controls.Button;
import net.montoyo.wd.client.gui.controls.Control;
import net.montoyo.wd.client.gui.controls.Label;
import net.montoyo.wd.client.gui.loading.FillControl;
import net.montoyo.wd.controls.builtin.ClickControl;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.net.WDNetworkRegistry;
import net.montoyo.wd.net.server_bound.C2SMessageScreenCtrl;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.Log;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.math.Vector2i;
import net.montoyo.wd.utilities.serialization.TypeData;
import net.montoyo.wd.utilities.serialization.Util;
import org.cef.browser.CefBrowser;
import org.cef.misc.CefCursorType;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import org.lwjgl.glfw.GLFW;
import org.vivecraft.client_vr.gameplay.VRPlayer;
import org.vivecraft.client_vr.gameplay.screenhandlers.KeyboardHandler;
//import org.vivecraft.gameplay.VRPlayer;
//import org.vivecraft.gameplay.screenhandlers.KeyboardHandler;
import java.io.*;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map;
import java.util.function.Consumer;
@OnlyIn(Dist.CLIENT)
public class GuiKeyboard extends WDScreen {
@ -42,6 +56,7 @@ public class GuiKeyboard extends WDScreen {
private ScreenBlockEntity tes;
private BlockSide side;
private ScreenData data;
private final ArrayList<TypeData> evStack = new ArrayList<>();
private BlockPos kbPos;
private boolean showWarning = true;
@ -69,7 +84,7 @@ public class GuiKeyboard extends WDScreen {
}
private static final boolean vivecraftPresent;
static {
boolean vivePres = false;
if (ModList.get().isLoaded("vivecraft")) vivePres = true;
@ -91,7 +106,7 @@ public class GuiKeyboard extends WDScreen {
}
vivecraftPresent = vivePres;
}
@Override
public void init() {
super.init();
@ -137,43 +152,68 @@ public class GuiKeyboard extends WDScreen {
defaultBackground = showWarning;
syncTicks = 5;
if (vivecraftPresent)
if (VRPlayer.get() != null)
KeyboardHandler.setOverlayShowing(true);
KeyboardCamera.focus(tes, side);
data = tes.getScreen(side);
CefBrowser browser = data.browser;
((MCEFBrowser) browser).setCursor(CefCursorType.fromId(data.mouseType));
((MCEFBrowser) browser).setCursorChangeListener((id) -> {
data.mouseType = id;
((MCEFBrowser) browser).setCursor(CefCursorType.fromId(id));
});
}
@Override
public void onClose() {
public void removed() {
super.removed();
if (vivecraftPresent)
if (VRPlayer.get() != null)
KeyboardHandler.setOverlayShowing(false);
super.onClose();
KeyboardCamera.focus(null, null);
CefBrowser browser = data.browser;
if (browser instanceof MCEFBrowser mcef) {
mcef.setCursor(CefCursorType.POINTER);
mcef.setCursorChangeListener((cursor) -> data.mouseType = cursor);
}
}
@Override
public void onClose() {
removed();
super.onClose();
this.minecraft.popGuiLayer();
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
if(quitOnEscape && keyCode == GLFW.GLFW_KEY_ESCAPE)
Minecraft.getInstance().setScreen(null);
if (quitOnEscape && keyCode == GLFW.GLFW_KEY_ESCAPE) {
onClose();
return true;
}
addKey(new TypeData(TypeData.Action.PRESS, keyCode, modifiers, scanCode));
return super.keyPressed(keyCode, scanCode, modifiers);
}
@Override
public boolean charTyped(char codePoint, int modifiers) {
addKey(new TypeData(TypeData.Action.TYPE, codePoint, modifiers, 0));
return super.charTyped(codePoint, modifiers);
}
@Override
public boolean keyReleased(int keyCode, int scanCode, int modifiers) {
addKey(new TypeData(TypeData.Action.RELEASE, keyCode, modifiers, scanCode));
return super.keyPressed(keyCode, scanCode, modifiers);
}
void addKey(TypeData data) {
tes.type(side, "[" + WebDisplays.GSON.toJson(data) + "]", kbPos);
evStack.add(data);
if (!evStack.isEmpty() && !syncRequested())
requestSync();
@ -243,4 +283,89 @@ public class GuiKeyboard extends WDScreen {
return bp.equals(kbPos) || (bp.equals(tes.getBlockPos()) && side == this.side);
}
protected void mouse(double mouseX, double mouseY, Consumer<Vector2i> func) {
float pct = Minecraft.getInstance().getPartialTick();
double fov = Minecraft.getInstance().gameRenderer.getFov(
Minecraft.getInstance().getEntityRenderDispatcher().camera,
pct, true
);
mouseX /= width;
mouseY /= height;
mouseX -= 0.5;
mouseY -= 0.5;
mouseY = -mouseY;
Matrix4f proj = Minecraft.getInstance().gameRenderer.getProjectionMatrix(fov);
Entity e = Minecraft.getInstance().getEntityRenderDispatcher().camera.getEntity();
PoseStack camera = new PoseStack();
float[] angle = KeyboardCamera.getAngle(e, pct);
camera.mulPose(Axis.XP.rotationDegrees(angle[0]));
camera.mulPose(Axis.YP.rotationDegrees(angle[1] + 180.0F));
Vector4f coord = new Vector4f(2f * (float) mouseX, 2 * (float) mouseY, 0, 1f);
coord.add(proj.invert().transform(coord));
coord = camera.last().pose().invert().transform(coord);
Vec3 vec3 = e.getEyePosition(pct);
Vec3 vec31 = new Vec3(coord.x, coord.y, coord.z).normalize();
BlockHitResult result = tes.trace(side, vec3, vec31);
if (result.getType() != HitResult.Type.MISS) {
tes.interact(result, func);
}
}
@Override
public void mouseMoved(double mouseX, double mouseY) {
mouse(mouseX, mouseY, (hit) -> {
tes.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserMove(tes, side, hit));
});
super.mouseMoved(mouseX, mouseY);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
mouse(mouseX, mouseY, (hit) -> {
tes.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
tes.handleMouseEvent(side, ClickControl.ControlType.DOWN, hit, button);
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserDown(tes, side, hit, button));
});
KeyboardCamera.setMouse(button, true);
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
mouse(mouseX, mouseY, (hit) -> {
tes.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
tes.handleMouseEvent(side, ClickControl.ControlType.UP, hit, button);
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserUp(tes, side, button));
});
KeyboardCamera.setMouse(button, false);
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public void tick() {
double mouseX = Minecraft.getInstance().mouseHandler.xpos() / Minecraft.getInstance().getWindow().getWidth();
double mouseY = Minecraft.getInstance().mouseHandler.ypos() / Minecraft.getInstance().getWindow().getHeight();
mouse(mouseX * width, mouseY * height, (hit) -> {
tes.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserMove(tes, side, hit));
});
super.tick();
}
}

View File

@ -5,16 +5,24 @@
package net.montoyo.wd.client.gui;
import com.cinemamod.mcef.MCEFBrowser;
import com.google.gson.JsonObject;
import com.mojang.blaze3d.platform.InputConstants;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.locale.Language;
import net.minecraft.network.chat.Component;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.client.ClientProxy;
import net.montoyo.wd.utilities.browser.WDBrowser;
import net.montoyo.wd.utilities.browser.handlers.js.Scripts;
import net.montoyo.wd.utilities.data.BlockSide;
import org.cef.misc.CefCursorType;
import org.lwjgl.glfw.GLFW;
@ -25,53 +33,53 @@ import static net.minecraftforge.api.distmarker.Dist.CLIENT;
@OnlyIn(CLIENT)
public class GuiMinePad extends WDScreen {
private ClientProxy.PadData pad;
private double vx;
private double vy;
private double vw;
private double vh;
public GuiMinePad() {
super(Component.nullToEmpty(null));
}
public GuiMinePad(ClientProxy.PadData pad) {
this();
this.pad = pad;
}
int trueWidth, trueHeight;
@Override
public void init() {
vw = ((double) width) - 32.0f;
vh = vw / WebDisplays.PAD_RATIO;
vx = 16.0f;
vy = (((double) height) - vh) / 2.0f;
trueWidth = width;
trueHeight = height;
this.width = (int) vw;
this.height = (int) vh;
super.init();
((MCEFBrowser) pad.view).setCursor(CefCursorType.fromId(pad.activeCursor));
((MCEFBrowser) pad.view).setCursorChangeListener((id) -> {
pad.activeCursor = id;
((MCEFBrowser) pad.view).setCursor(CefCursorType.fromId(id));
});
}
private static void addRect(BufferBuilder bb, double x, double y, double w, double h) {
bb.vertex(x, y, 0.0).color(255, 255, 255, 255).endVertex();
bb.vertex(x + w, y, 0.0).color(255, 255, 255, 255).endVertex();
bb.vertex(x + w, y + h, 0.0).color(255, 255, 255, 255).endVertex();
bb.vertex(x, y + h, 0.0).color(255, 255, 255, 255).endVertex();
}
@Override
public void render(GuiGraphics graphics, int mouseX, int mouseY, float ptt) {
width = trueWidth;
@ -79,10 +87,10 @@ public class GuiMinePad extends WDScreen {
renderBackground(graphics);
width = (int) vw;
height = (int) vh;
RenderSystem.disableCull();
RenderSystem.setShaderColor(0.73f, 0.73f, 0.73f, 1.0f);
Tesselator t = Tesselator.getInstance();
BufferBuilder bb = t.getBuilder();
bb.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR);
@ -91,7 +99,7 @@ public class GuiMinePad extends WDScreen {
addRect(bb, vx - 16, vy, 16, vh);
addRect(bb, vx + vw, vy, 16, vh);
t.end();
if (pad.view != null) {
// pad.view.draw(poseStack, vx, vy + vh, vx + vw, vy);
RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
@ -112,20 +120,26 @@ public class GuiMinePad extends WDScreen {
t.end();
RenderSystem.enableDepthTest();
}
RenderSystem.enableCull();
graphics.drawString(
minecraft.font, Language.getInstance().getOrDefault(
"webdisplays.gui.minepad.close"
), (int) vx + 4, (int) vy - minecraft.font.lineHeight - 3, 16777215, true
);
}
@Override
public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
return this.keyChanged(keyCode, scanCode, modifiers, true) || super.keyPressed(keyCode, scanCode, modifiers);
}
@Override
public boolean keyReleased(int keyCode, int scanCode, int modifiers) {
return this.keyChanged(keyCode, scanCode, modifiers, false) || super.keyReleased(keyCode, scanCode, modifiers);
}
@Override
public boolean charTyped(char codePoint, int modifiers) {
if (pad.view != null) {
@ -135,60 +149,60 @@ public class GuiMinePad extends WDScreen {
return super.charTyped(codePoint, modifiers);
}
}
/* copied from MCEF */
public boolean keyChanged(int keyCode, int scanCode, int modifiers, boolean pressed) {
assert minecraft != null;
if (keyCode == GLFW.GLFW_KEY_ESCAPE) {
minecraft.setScreen(null);
if ((modifiers & GLFW.GLFW_MOD_SHIFT) == GLFW.GLFW_MOD_SHIFT && keyCode == GLFW.GLFW_KEY_ESCAPE) {
onClose();
return true;
}
InputConstants.Key iuKey = InputConstants.getKey(keyCode, scanCode);
String keystr = iuKey.getDisplayName().getString();
// System.out.println("KEY STR " + keystr);
if (keystr.length() == 0)
return false;
char key = keystr.charAt(keystr.length() - 1);
if (keystr.equals("Enter")) {
keyCode = 10;
key = '\n';
}
if (pad.view != null) {
if (pressed)
((MCEFBrowser) pad.view).sendKeyPress(keyCode, scanCode, modifiers);
else
((MCEFBrowser) pad.view).sendKeyRelease(keyCode, scanCode, modifiers);
if (pressed && key == '\n')
if (modifiers != 0) ((MCEFBrowser) pad.view).sendKeyTyped('\r', modifiers);
return true;
}
return false;
}
@Override
public void mouseMoved(double mouseX, double mouseY) {
super.mouseMoved(mouseX, mouseY);
mouse(-1, false, (int) mouseX, (int) mouseY, 0);
}
@Override
public boolean mouseClicked(double mouseX, double mouseY, int button) {
mouse(button, true, (int) mouseX, (int) mouseY, 0);
return super.mouseClicked(mouseX, mouseY, button);
}
@Override
public boolean mouseReleased(double mouseX, double mouseY, int button) {
mouse(button, false, (int) mouseX, (int) mouseY, 0);
return super.mouseReleased(mouseX, mouseY, button);
}
@Override
public boolean mouseScrolled(double mouseX, double mouseY, double amount) {
double mx = (mouseX - vx) / vw;
@ -197,29 +211,63 @@ public class GuiMinePad extends WDScreen {
int sy = (int) (my * WebDisplays.INSTANCE.padResY);
// TODO: this doesn't work, and I don't understand why?
((MCEFBrowser) pad.view).sendMouseWheel(sx, sy, amount, (hasControlDown() && !hasAltDown() && !hasShiftDown()) ? GLFW.GLFW_MOD_CONTROL : 0);
return super.mouseScrolled(mouseX, mouseY, amount);
}
public void capturedMouse(double scaledX, double scaledY, int sx, int sy) {
double centerX = (int) (0.5 * (double) this.minecraft.getWindow().getGuiScaledWidth());
double centerY = (int) (0.5 * (double) this.minecraft.getWindow().getGuiScaledHeight());
if (sx == (int) centerX && sy == (int) centerY) return;
double mx = (centerX - vx) / vw;
double my = (centerY - vy) / vh;
double scaledCentX = (mx * WebDisplays.INSTANCE.padResX);
double scaledCentY = (my * WebDisplays.INSTANCE.padResY);
double deltX = scaledX - scaledCentX;
double deltY = scaledY - scaledCentY;
String scr = Scripts.MOUSE_EVENT;
pad.view.executeJavaScript(
scr
.replace("%xCoord%", "" + (int) centerX)
.replace("%yCoord%", "" + (int) centerY)
.replace("%xDelta%", "" + (deltX))
.replace("%yDelta%", "" + (deltY)),
"WebDisplays", 0
);
// lock mouse
try {
double xpos = (this.minecraft.getWindow().getScreenWidth() / 2);
double ypos = (this.minecraft.getWindow().getScreenHeight() / 2);
GLFW.glfwSetCursorPos(minecraft.getWindow().getWindow(), xpos, ypos);
} catch (Throwable ignored) {
}
}
public void mouse(int btn, boolean pressed, int sx, int sy, double scrollAmount) {
double mx = (sx - vx) / vw;
double my = (sy - vy) / vh;
if (pad.view != null && mx >= 0 && mx <= 1) {
//Scale again according to the webview
sx = (int) (mx * WebDisplays.INSTANCE.padResX);
sy = (int) (my * WebDisplays.INSTANCE.padResY);
if (btn == -1)
((MCEFBrowser) pad.view).sendMouseMove(sx, sy);
else if (pressed)
((MCEFBrowser) pad.view).sendMousePress(sx, sy, btn);
else
((MCEFBrowser) pad.view).sendMouseRelease(sx, sy, btn);
int scaledX = (int) (mx * WebDisplays.INSTANCE.padResX);
int scaledY = (int) (my * WebDisplays.INSTANCE.padResY);
if (btn == -1) {
if (locked)
capturedMouse(mx * WebDisplays.INSTANCE.padResX, my * WebDisplays.INSTANCE.padResY, sx, sy);
else ((MCEFBrowser) pad.view).sendMouseMove(scaledX, scaledY);
} else if (pressed)
((MCEFBrowser) pad.view).sendMousePress(scaledX, scaledY, btn);
else ((MCEFBrowser) pad.view).sendMouseRelease(scaledX, scaledY, btn);
pad.view.setFocus(true);
}
}
public static Optional<Character> getChar(int keyCode, int scanCode) {
String keystr = GLFW.glfwGetKeyName(keyCode, scanCode);
if (keystr == null) {
@ -231,24 +279,29 @@ public class GuiMinePad extends WDScreen {
if (keystr.length() == 0) {
return Optional.empty();
}
return Optional.of(keystr.charAt(keystr.length() - 1));
}
@Override
public void tick() {
if (pad.view == null)
minecraft.setScreen(null); //In case the user dies with the pad in the hand
pollElement();
}
@Override
public boolean isForBlock(BlockPos bp, BlockSide side) {
return false;
}
@Override
public void removed() {
super.removed();
InputConstants.updateRawMouseInput(
minecraft.getWindow().getWindow(),
Minecraft.getInstance().options.rawMouseInput().get()
);
if (pad.view instanceof MCEFBrowser browser) {
browser.setCursor(CefCursorType.POINTER);
browser.setCursorChangeListener((cursor) -> {
@ -256,10 +309,49 @@ public class GuiMinePad extends WDScreen {
});
}
}
@Override
public void onClose() {
super.onClose();
removed();
this.minecraft.popGuiLayer();
}
boolean locked = false;
double lockCenterX = -1;
double lockCenterY = -1;
protected void updateCrd(JsonObject obj) {
if (obj.getAsJsonPrimitive("exists").getAsBoolean()) {
locked = true;
RenderSystem.recordRenderCall(() -> {
InputConstants.updateRawMouseInput(
minecraft.getWindow().getWindow(),
obj.getAsJsonPrimitive("unadjust").getAsBoolean()
);
GLFW.glfwSetInputMode(Minecraft.getInstance().getWindow().getWindow(), 208897, GLFW.GLFW_CURSOR_DISABLED);
});
lockCenterX = obj.getAsJsonPrimitive("x").getAsDouble() + obj.getAsJsonPrimitive("w").getAsDouble() / 2;
lockCenterY = obj.getAsJsonPrimitive("y").getAsDouble() + obj.getAsJsonPrimitive("h").getAsDouble() / 2;
} else {
if (locked) {
locked = false;
RenderSystem.recordRenderCall(()->{
InputConstants.updateRawMouseInput(
minecraft.getWindow().getWindow(),
Minecraft.getInstance().options.rawMouseInput().get()
);
GLFW.glfwSetInputMode(Minecraft.getInstance().getWindow().getWindow(), 208897, GLFW.GLFW_CURSOR_NORMAL);
GLFW.glfwSetCursor(Minecraft.getInstance().getWindow().getWindow(), CefCursorType.fromId(pad.activeCursor).glfwId);
});
}
}
}
protected void pollElement() {
if (pad.view instanceof WDBrowser browser) {
JsonObject object = browser.pointerLockElement().getObj();
if (object != null) updateCrd(object);
}
}
}

View File

@ -15,6 +15,7 @@ import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.client.gui.controls.*;
import net.montoyo.wd.client.gui.loading.FillControl;
import net.montoyo.wd.core.ScreenRights;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.item.WDItem;
import net.montoyo.wd.net.WDNetworkRegistry;
@ -151,7 +152,7 @@ public class GuiScreenConfig extends WDScreen {
boxOClick.setUserdata(ScreenRights.INTERACT);
boxOSetUrl.setUserdata(ScreenRights.CHANGE_URL);
ScreenBlockEntity.Screen scr = tes.getScreen(side);
ScreenData scr = tes.getScreen(side);
if(scr != null) {
owner = scr.owner;
rotation = scr.rotation;
@ -195,7 +196,7 @@ public class GuiScreenConfig extends WDScreen {
}
private void clickSetRes() {
ScreenBlockEntity.Screen scr = tes.getScreen(side);
ScreenData scr = tes.getScreen(side);
if(scr == null)
return; //WHATDAFUQ?

View File

@ -0,0 +1,273 @@
package net.montoyo.wd.client.gui.camera;
import net.minecraft.client.Minecraft;
import net.minecraft.commands.arguments.EntityAnchorArgument;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec2;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.client.event.ViewportEvent;
import net.minecraftforge.event.TickEvent;
import net.montoyo.wd.client.gui.GuiKeyboard;
import net.montoyo.wd.config.ClientConfig;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.utilities.browser.WDBrowser;
import net.montoyo.wd.utilities.browser.handlers.js.queries.ElementCenterQuery;
import net.montoyo.wd.utilities.data.BlockSide;
public class KeyboardCamera {
private static ScreenBlockEntity tes;
private static BlockSide side;
private static double oxCrd = -1;
private static double xCrd = -1;
private static double nxCrd = -1;
private static double oyCrd = -1;
private static double yCrd = -1;
private static double nyCrd = -1;
private static double nextX = -1;
private static double nextY = -1;
private static double focalX = -1;
private static double focalY = -1;
private static final boolean[] mouseStatus = new boolean[2];
protected static Vec2 pxToHit(ScreenData scr, Vec2 dst) {
float cx, cy;
if (scr.rotation.isVertical) {
cy = dst.x;
cx = dst.y;
} else {
cx = dst.x;
cy = dst.y;
}
cx /= (float) scr.resolution.x;
cy /= (float) scr.resolution.y;
switch (scr.rotation) {
case ROT_270:
cx = 1.0f - cx;
break;
case ROT_180:
cx = 1.0f - cx;
cy = 1.0f - cy;
break;
case ROT_90:
cy = 1.0f - cy;
break;
}
if (side != BlockSide.BOTTOM)
cy = 1.0f - cy;
float swInverse = (((float) scr.size.x) - 4.f / 16.f);
float shInverse = (((float) scr.size.y) - 4.f / 16.f);
cx *= swInverse;
cy *= shInverse;
if (side.right.x > 0 || side.right.z > 0)
cx += 1.f;
if (side == BlockSide.TOP || side == BlockSide.BOTTOM)
cy -= 1.f;
return new Vec2(cx + (2 / 16f), cy + (2 / 16f));
}
protected static void updateCrd(ElementCenterQuery lock) {
ScreenData scr = tes.getScreen(side);
if (scr != null) {
Vec2 c;
if (!mouseStatus[0] && !mouseStatus[1]) {
if (lock.hasFocused()) {
if (ClientConfig.Input.keyboardCamera) {
nextX = lock.getX();
nextY = lock.getY();
c = pxToHit(scr, new Vec2((float) nextX, (float) nextY));
} else c = new Vec2(scr.size.x / 2f, scr.size.y / 2f);
} else c = new Vec2(scr.size.x / 2f, scr.size.y / 2f);
// } else c = new Vec2((float) focalX, (float) focalY);
} else return;
focalX = c.x;
focalY = c.y;
nextX = c.x;
nextY = c.y;
if (nextX < 0) nextX = 0;
else if (nextX > scr.size.x) nextX = scr.size.x;
if (nextY < 0) nextY = 0;
else if (nextY > scr.size.y) nextY = scr.size.y;
float scl = Math.max(scr.size.x, scr.size.y);
double mx = Minecraft.getInstance().mouseHandler.xpos();
mx /= Minecraft.getInstance().getWindow().getWidth();
double my = Minecraft.getInstance().mouseHandler.ypos();
my /= Minecraft.getInstance().getWindow().getHeight();
Vec2 v2 = new Vec2((float) mx, (float) my).add(-0.5f);
nextX += v2.x * scl;
nextY -= v2.y * scl;
}
}
protected static void pollElement() {
ScreenBlockEntity teTmp = tes;
BlockSide sdTmp = side;
// async nonsense can occur here
if (teTmp == null || sdTmp == null) return;
ScreenData scr = teTmp.getScreen(sdTmp);
if (scr != null) {
if (scr.browser instanceof WDBrowser wdBrowser) {
wdBrowser.focusedElement().dispatch(scr.browser);
updateCrd(((WDBrowser) scr.browser).focusedElement());
}
}
}
public static float[] getAngle(Entity e, double pct) {
BlockEntity tes = KeyboardCamera.tes;
BlockSide side = KeyboardCamera.side;
if (tes == null) return new float[]{Float.NaN, 0};
if (side == null) return new float[]{Float.NaN, 0};
double coxCrd = Mth.lerp(0.5 * pct, oxCrd, xCrd);
double coyCrd = Mth.lerp(0.5 * pct, oyCrd, yCrd);
double focalX = tes.getBlockPos().getX() +
side.right.x * (coxCrd - 1) + side.up.x * coyCrd + Math.abs(side.forward.x) * 0.5;
double focalY = tes.getBlockPos().getY() +
side.right.y * (coxCrd - 1) + side.up.y * coyCrd + Math.abs(side.forward.y) * 0.5;
double focalZ = tes.getBlockPos().getZ() +
side.right.z * (coxCrd - 1) + side.up.z * coyCrd + Math.abs(side.forward.z) * 0.5;
focalX += side.forward.x * 0.5f;
focalY += side.forward.y * 0.5f;
focalZ += side.forward.z * 0.5f;
float[] angle = lookAt(
e, EntityAnchorArgument.Anchor.EYES,
new Vec3(focalX, focalY, focalZ)
);
return angle;
}
public static void setMouse(int side, boolean pressed) {
mouseStatus[side] = pressed;
}
public static void updateCamera(ViewportEvent.ComputeCameraAngles event) {
if (tes == null) {
xCrd = -1;
yCrd = -1;
return; // nothing to do
}
if (xCrd == -1) return;
if (yCrd == -1) return;
float[] angle = getAngle(event.getCamera().getEntity(), event.getPartialTick());
if (Float.isNaN(angle[0])) return;
// float xRot = event.getYaw(); // left right
// float yRot = event.getPitch(); // up down
// TODO: smooth in/out
event.setYaw(angle[1]);
event.setPitch(angle[0]);
}
public static void focus(ScreenBlockEntity screen, BlockSide side) {
KeyboardCamera.tes = screen;
KeyboardCamera.side = side;
}
public static float[] lookAt(Entity entity, EntityAnchorArgument.Anchor pAnchor, Vec3 pTarget) {
Vec3 vec3 = pAnchor.apply(entity);
double d0 = pTarget.x - vec3.x;
double d1 = pTarget.y - vec3.y;
double d2 = pTarget.z - vec3.z;
double d3 = Math.sqrt(d0 * d0 + d2 * d2);
float xr = (Mth.wrapDegrees((float) (-(Mth.atan2(d1, d3) * (double) (180F / (float) Math.PI)))));
float yr = (Mth.wrapDegrees((float) (Mth.atan2(d2, d0) * (double) (180F / (float) Math.PI)) - 90.0F));
return new float[]{xr, yr};
}
protected static int delay = 8;
public static void gameTick(TickEvent.ClientTickEvent event) {
if (mouseStatus[0] || mouseStatus[1]) {
oxCrd = Mth.lerp(0.5, oxCrd, xCrd);
oyCrd = Mth.lerp(0.5, oyCrd, yCrd);
return;
}
if (event.phase.equals(TickEvent.Phase.END)) {
if (side == null) {
delay = 1;
oxCrd = -1;
oyCrd = -1;
xCrd = -1;
yCrd = -1;
nxCrd = -1;
nyCrd = -1;
return;
}
if (!(Minecraft.getInstance().screen instanceof GuiKeyboard)) {
tes = null;
side = null;
return;
}
pollElement();
double anxx = nextX;
double anxy = nextY;
if (
anxx == -1 || anxy == -1 ||
nxCrd == -1 || nyCrd == -1 ||
oxCrd == -1 || oyCrd == -1 ||
xCrd == -1 || yCrd == -1
) {
ScreenData data = tes.getScreen(side);
if (data == null)
return;
anxx = data.size.x / 2.0;
anxy = data.size.y / 2.0;
if (nxCrd == -1) {
oxCrd = xCrd = anxx;
oyCrd = yCrd = anxy;
}
}
nxCrd = anxx;
nyCrd = anxy;
oxCrd = Mth.lerp(0.5, oxCrd, xCrd);
xCrd = Mth.lerp(0.15, xCrd, nxCrd);
oyCrd = Mth.lerp(0.5, oyCrd, yCrd);
yCrd = Mth.lerp(0.15, yCrd, nyCrd);
}
}
}

View File

@ -7,11 +7,14 @@ package net.montoyo.wd.client.renderers;
import com.cinemamod.mcef.MCEFBrowser;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.*;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.utilities.math.Vector3f;
import net.montoyo.wd.utilities.math.Vector3i;
import org.jetbrains.annotations.NotNull;
@ -37,16 +40,19 @@ public class ScreenRenderer implements BlockEntityRenderer<ScreenBlockEntity> {
public void render(ScreenBlockEntity te, float partialTick, @NotNull PoseStack poseStack, @NotNull MultiBufferSource bufferSource, int packedLight, int packedOverlay) {
if (!te.isLoaded())
return;
//Disable lighting
// RenderSystem.enableTexture();
// RenderSystem.disableCull();
// RenderSystem.disableCull();
RenderSystem.disableBlend();
for (int i = 0; i < te.screenCount(); i++) {
ScreenBlockEntity.Screen scr = te.getScreen(i);
ScreenData scr = te.getScreen(i);
if (scr.browser == null) {
scr.createBrowser(true);
double dist = WebDisplays.PROXY.distanceTo(te, Minecraft.getInstance().getEntityRenderDispatcher().camera.getPosition());
if (dist <= WebDisplays.INSTANCE.loadDistance2 * 16)
scr.createBrowser(te, true);
else continue;
}
// TODO: manually backface cull the screens

View File

@ -38,7 +38,7 @@ public class ClientConfig {
"Due to how web browsers work however, the larger this value is, the smaller text is",
"Also, higher values will invariably lag more",
"A good goto value for this would be the height of your monitor, in pixels",
"A standard monitor is (at least currently) 1080"
"A standard monitor is (at least currently) 1080",
})
@Translation("config.webdisplays.pad_res")
@IntRange(minV = 0, maxV = Integer.MAX_VALUE)
@ -53,55 +53,70 @@ public class ClientConfig {
@Translation("config.webdisplays.side_pad")
@Default(valueBoolean = true)
public static boolean sidePad = true;
@Name("switch_buttons")
@Comment("If the left and right buttons should be swapped when using a laser")
@Translation("config.webdisplays.switch_buttons")
@DoubleRange(minV = 0, maxV = Double.MAX_VALUE)
@Default(valueD = 30)
public static boolean switchButtons = true;
@Comment({
"AutoVolume makes audio fade off based on distance",
"Currently, this seems to not work"
"Options relating to input handling"
})
@CFGSegment("auto_volume")
public static class AutoVolumeControl {
@Name("enabled")
@Comment("Whether or not auto volume should be enabled")
@Translation("config.webdisplays.auto_vol")
@CFGSegment("input")
public static class Input {
@Name("keyboard_camera")
@Comment({
"If this is on, then the camera will try to focus on the selected element while a keyboard is in use",
"Elsewise, it'll try to focus on the center of the screen",
})
@Translation("config.webdisplays.keyboard_camera")
@Default(valueBoolean = true)
public static boolean enableAutoVolume = true;
@Name("youtube_volume")
@Comment("How loud youtube should be by default")
@Translation("config.webdisplays.yt_vol")
@DoubleRange(minV = 0, maxV = 100)
@Default(valueD = 100)
public static double ytVolume = 100.0;
@Name("dist0")
@Comment("Distance after which you can't hear anything (in blocks)")
@Translation("config.webdisplays.d0")
public static boolean keyboardCamera = true;
@Name("switch_buttons")
@Comment("If the left and right buttons should be swapped when using a laser")
@Translation("config.webdisplays.switch_buttons")
@DoubleRange(minV = 0, maxV = Double.MAX_VALUE)
@Default(valueD = 30)
public static double dist0 = 30.0;
@Name("dist100")
@Comment("Distance after which the sound starts dropping (in blocks)")
@Translation("config.webdisplays.d100")
@DoubleRange(minV = 0, maxV = Double.MAX_VALUE)
@Default(valueD = 10)
public static double dist100 = 10.0;
public static boolean switchButtons = true;
}
// @Comment({
// "AutoVolume makes audio fade off based on distance",
// "Currently, this seems to not work"
// })
// @CFGSegment("auto_volume")
// public static class AutoVolumeControl {
// @Name("enabled")
// @Comment("Whether or not auto volume should be enabled")
// @Translation("config.webdisplays.auto_vol")
// @Default(valueBoolean = true)
// public static boolean enableAutoVolume = true;
//
// @Name("youtube_volume")
// @Comment("How loud youtube should be by default")
// @Translation("config.webdisplays.yt_vol")
// @DoubleRange(minV = 0, maxV = 100)
// @Default(valueD = 100)
// public static double ytVolume = 100.0;
//
// @Name("dist0")
// @Comment("Distance after which you can't hear anything (in blocks)")
// @Translation("config.webdisplays.d0")
// @DoubleRange(minV = 0, maxV = Double.MAX_VALUE)
// @Default(valueD = 30)
// public static double dist0 = 30.0;
//
// @Name("dist100")
// @Comment("Distance after which the sound starts dropping (in blocks)")
// @Translation("config.webdisplays.d100")
// @DoubleRange(minV = 0, maxV = Double.MAX_VALUE)
// @Default(valueD = 10)
// public static double dist100 = 10.0;
// }
@SuppressWarnings("unused")
public static void postLoad() {
if (unloadDistance < loadDistance + 2.0)
unloadDistance = loadDistance + 2.0;
if (AutoVolumeControl.dist0 < AutoVolumeControl.dist100 + 0.1)
AutoVolumeControl.dist0 = AutoVolumeControl.dist100 + 0.1;
// if (AutoVolumeControl.dist0 < AutoVolumeControl.dist100 + 0.1)
// AutoVolumeControl.dist0 = AutoVolumeControl.dist100 + 0.1;
// cache pad resolution
WebDisplays.INSTANCE.padResY = padResolution;
@ -111,8 +126,8 @@ public class ClientConfig {
WebDisplays.INSTANCE.unloadDistance2 = unloadDistance * unloadDistance;
WebDisplays.INSTANCE.loadDistance2 = loadDistance * loadDistance;
WebDisplays.INSTANCE.ytVolume = (float) AutoVolumeControl.ytVolume;
WebDisplays.INSTANCE.avDist100 = (float) AutoVolumeControl.dist100;
WebDisplays.INSTANCE.avDist0 = (float) AutoVolumeControl.dist0;
// WebDisplays.INSTANCE.ytVolume = (float) AutoVolumeControl.ytVolume;
// WebDisplays.INSTANCE.avDist100 = (float) AutoVolumeControl.dist100;
// WebDisplays.INSTANCE.avDist0 = (float) AutoVolumeControl.dist0;
}
}

View File

@ -18,13 +18,18 @@ public class CommonConfig {
public static void init() {
// loads the class
}
@Name("hard_recipes")
@Comment("If true, breaking the minePad is required to craft upgrades.")
@Translation("config.webdisplays.hard_recipes")
@IntRange(minV = 0, maxV = Integer.MAX_VALUE)
@Default(valueBoolean = true)
public static boolean hardRecipes = true;
@Name("join_message")
@Comment("Whether or not webdisplays should thank the user for using the mod")
@Translation("config.webdisplays.join_message")
@Default(valueBoolean = true)
public static boolean joinMessage = true;
@Name("disable_ownership_thief")
@Comment("If true, the ownership thief item will be disabled")

View File

@ -11,6 +11,7 @@ import net.minecraftforge.network.NetworkEvent;
import net.montoyo.wd.controls.ScreenControl;
import net.montoyo.wd.core.MissingPermissionException;
import net.montoyo.wd.core.ScreenRights;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.utilities.data.BlockSide;
@ -89,7 +90,7 @@ public class ManageRightsAndUpdgradesControl extends ScreenControl {
else tes.removeUpgrade(side, toRemove, player);
}
case RIGHTS -> {
ScreenBlockEntity.Screen scr = tes.getScreen(side);
ScreenData scr = tes.getScreen(side);
int fr = scr.owner.uuid.equals(player.getGameProfile().getId()) ? friendRights : scr.friendRights;
int or = (scr.rightsFor(player) & ScreenRights.MANAGE_OTHER_RIGHTS) == 0 ? scr.otherRights : otherRights;
@ -111,7 +112,7 @@ public class ManageRightsAndUpdgradesControl extends ScreenControl {
else tes.removeUpgrade(side, toRemove, player);
}
case RIGHTS -> {
ScreenBlockEntity.Screen scr = tes.getScreen(side);
ScreenData scr = tes.getScreen(side);
int fr = friendRights;
int or = otherRights;

View File

@ -13,6 +13,7 @@ import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.network.PacketDistributor;
import net.montoyo.wd.client.gui.GuiScreenConfig;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.net.BufferUtils;
import net.montoyo.wd.net.WDNetworkRegistry;
@ -33,74 +34,74 @@ public class ScreenConfigData extends GuiData {
public ScreenConfigData() {
}
public ScreenConfigData(Vector3i pos, BlockSide side, ScreenBlockEntity.Screen scr) {
this.pos = pos;
this.side = side;
friends = scr.friends.toArray(new NameUUIDPair[0]);
friendRights = scr.friendRights;
otherRights = scr.otherRights;
onlyUpdate = false;
}
@OnlyIn(Dist.CLIENT)
@Override
public Screen createGui(Screen old, Level world) {
if (old != null && old instanceof GuiScreenConfig) {
GuiScreenConfig gsc = (GuiScreenConfig) old;
if (gsc.isForBlock(pos.toBlock(), side)) {
gsc.updateFriends(friends);
gsc.updateFriendRights(friendRights);
gsc.updateOtherRights(otherRights);
gsc.updateMyRights();
return null;
}
}
if (onlyUpdate)
return null;
BlockEntity te = world.getBlockEntity(pos.toBlock());
if (te == null || !(te instanceof ScreenBlockEntity)) {
Log.error("TileEntity at %s is not a screen; can't open gui!", pos.toString());
return null;
}
return new GuiScreenConfig(Component.nullToEmpty(""), (ScreenBlockEntity) te, side, friends, friendRights, otherRights);
}
@Override
public String getName() {
return "ScreenConfig";
}
public ScreenConfigData updateOnly() {
onlyUpdate = true;
return this;
}
public void sendTo(PacketDistributor.TargetPoint tp) {
WDNetworkRegistry.INSTANCE.send(PacketDistributor.NEAR.with(() -> tp), new S2CMessageOpenGui(this));
}
@Override
public void serialize(FriendlyByteBuf buf) {
buf.writeBoolean(onlyUpdate);
BufferUtils.writeVec3i(buf, pos);
BufferUtils.writeEnum(buf, side, (byte) 1);
BufferUtils.writeArray(buf, friends, (nameUUIDPair) -> nameUUIDPair.writeTo(buf));
buf.writeInt(friendRights);
buf.writeInt(otherRights);
}
@Override
public void deserialize(FriendlyByteBuf buf) {
onlyUpdate = buf.readBoolean();
pos = BufferUtils.readVec3i(buf);
side = (BlockSide) BufferUtils.readEnum(buf, (v) -> BlockSide.values()[v], (byte) 1);
friends = BufferUtils.readArray(buf, new NameUUIDPair[0], () -> new NameUUIDPair(buf));
friendRights = buf.readInt();
otherRights = buf.readInt();
}
public ScreenConfigData(Vector3i pos, BlockSide side, ScreenData scr) {
this.pos = pos;
this.side = side;
friends = scr.friends.toArray(new NameUUIDPair[0]);
friendRights = scr.friendRights;
otherRights = scr.otherRights;
onlyUpdate = false;
}
@OnlyIn(Dist.CLIENT)
@Override
public Screen createGui(Screen old, Level world) {
if (old != null && old instanceof GuiScreenConfig) {
GuiScreenConfig gsc = (GuiScreenConfig) old;
if (gsc.isForBlock(pos.toBlock(), side)) {
gsc.updateFriends(friends);
gsc.updateFriendRights(friendRights);
gsc.updateOtherRights(otherRights);
gsc.updateMyRights();
return null;
}
}
if (onlyUpdate)
return null;
BlockEntity te = world.getBlockEntity(pos.toBlock());
if (te == null || !(te instanceof ScreenBlockEntity)) {
Log.error("TileEntity at %s is not a screen; can't open gui!", pos.toString());
return null;
}
return new GuiScreenConfig(Component.nullToEmpty(""), (ScreenBlockEntity) te, side, friends, friendRights, otherRights);
}
@Override
public String getName() {
return "ScreenConfig";
}
public ScreenConfigData updateOnly() {
onlyUpdate = true;
return this;
}
public void sendTo(PacketDistributor.TargetPoint tp) {
WDNetworkRegistry.INSTANCE.send(PacketDistributor.NEAR.with(() -> tp), new S2CMessageOpenGui(this));
}
@Override
public void serialize(FriendlyByteBuf buf) {
buf.writeBoolean(onlyUpdate);
BufferUtils.writeVec3i(buf, pos);
BufferUtils.writeEnum(buf, side, (byte) 1);
BufferUtils.writeArray(buf, friends, (nameUUIDPair) -> nameUUIDPair.writeTo(buf));
buf.writeInt(friendRights);
buf.writeInt(otherRights);
}
@Override
public void deserialize(FriendlyByteBuf buf) {
onlyUpdate = buf.readBoolean();
pos = BufferUtils.readVec3i(buf);
side = (BlockSide) BufferUtils.readEnum(buf, (v) -> BlockSide.values()[v], (byte) 1);
friends = BufferUtils.readArray(buf, new NameUUIDPair[0], () -> new NameUUIDPair(buf));
friendRights = buf.readInt();
otherRights = buf.readInt();
}
}

View File

@ -214,7 +214,7 @@ public abstract class AbstractInterfaceBlockEntity extends AbstractPeripheralBlo
if(owner == null || scr == null)
return err("notlinked");
else {
ScreenBlockEntity.Screen scrscr = scr.getScreen(screenSide);
ScreenData scrscr = scr.getScreen(screenSide);
if((scrscr.rightsFor(owner.uuid) & ScreenRights.INTERACT) == 0)
return err("restrictions");

View File

@ -41,7 +41,7 @@ public class KeyboardBlockEntity extends AbstractPeripheralBlockEntity {
return InteractionResult.SUCCESS;
}
ScreenBlockEntity.Screen scr = tes.getScreen(screenSide);
ScreenData scr = tes.getScreen(screenSide);
if((scr.rightsFor(player) & ScreenRights.INTERACT) == 0) {
Util.toast(player, "restrictions");
return InteractionResult.SUCCESS;
@ -58,7 +58,7 @@ public class KeyboardBlockEntity extends AbstractPeripheralBlockEntity {
ScreenBlockEntity tes = getConnectedScreen();
if(tes != null) {
ScreenBlockEntity.Screen scr = tes.getScreen(screenSide);
ScreenData scr = tes.getScreen(screenSide);
boolean ok;
if(ent instanceof Player)

View File

@ -61,8 +61,8 @@ public class RedstoneControlBlockEntity extends AbstractPeripheralBlockEntity {
return InteractionResult.SUCCESS;
}
ScreenBlockEntity.Screen scr = tes.getScreen(screenSide);
if ((scr.rightsFor(player) & ScreenRights.CHANGE_URL) == 0) {
ScreenData scr = tes.getScreen(screenSide);
if((scr.rightsFor(player) & ScreenRights.CHANGE_URL) == 0) {
Util.toast(player, "restrictions");
return InteractionResult.SUCCESS;
}

View File

@ -36,8 +36,8 @@ public class RemoteControlBlockEntity extends AbstractPeripheralBlockEntity {
return InteractionResult.SUCCESS;
}
ScreenBlockEntity.Screen scr = tes.getScreen(screenSide);
if ((scr.rightsFor(player) & ScreenRights.CHANGE_URL) == 0) {
ScreenData scr = tes.getScreen(screenSide);
if((scr.rightsFor(player) & ScreenRights.CHANGE_URL) == 0) {
Util.toast(player, "restrictions");
return InteractionResult.SUCCESS;
}

View File

@ -4,9 +4,8 @@
package net.montoyo.wd.entity;
import com.cinemamod.mcef.MCEF;
import com.cinemamod.mcef.MCEFBrowser;
import com.cinemamod.mcef.listeners.MCEFCursorChangeListener;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
@ -19,10 +18,12 @@ import net.minecraft.world.InteractionHand;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.network.PacketDistributor;
import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.block.ScreenBlock;
@ -33,20 +34,23 @@ import net.montoyo.wd.core.DefaultUpgrade;
import net.montoyo.wd.core.IUpgrade;
import net.montoyo.wd.core.ScreenRights;
import net.montoyo.wd.data.ScreenConfigData;
import net.montoyo.wd.registry.BlockRegistry;
import net.montoyo.wd.registry.ItemRegistry;
import net.montoyo.wd.registry.TileRegistry;
import net.montoyo.wd.miniserv.SyncPlugin;
import net.montoyo.wd.net.WDNetworkRegistry;
import net.montoyo.wd.net.client_bound.S2CMessageAddScreen;
import net.montoyo.wd.net.client_bound.S2CMessageScreenUpdate;
import net.montoyo.wd.utilities.*;
import net.montoyo.wd.registry.BlockRegistry;
import net.montoyo.wd.registry.ItemRegistry;
import net.montoyo.wd.registry.TileRegistry;
import net.montoyo.wd.utilities.Log;
import net.montoyo.wd.utilities.Multiblock;
import net.montoyo.wd.utilities.ScreenIterator;
import net.montoyo.wd.utilities.VideoType;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.data.Rotation;
import net.montoyo.wd.utilities.math.MutableAABB;
import net.montoyo.wd.utilities.math.Vector2i;
import net.montoyo.wd.utilities.math.Vector3f;
import net.montoyo.wd.utilities.math.Vector3i;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.data.Rotation;
import net.montoyo.wd.utilities.serialization.NameUUIDPair;
import net.montoyo.wd.utilities.serialization.TypeData;
import org.cef.browser.CefBrowser;
@ -55,8 +59,8 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
import static net.montoyo.wd.block.PeripheralBlock.point;
@ -66,191 +70,8 @@ public class ScreenBlockEntity extends BlockEntity {
super(TileRegistry.SCREEN_BLOCK_ENTITY.get(), arg2, arg3);
}
public static class Screen {
public BlockSide side;
public Vector2i size;
public Vector2i resolution;
public Rotation rotation = Rotation.ROT_0;
public String url;
private VideoType videoType;
public NameUUIDPair owner;
public ArrayList<NameUUIDPair> friends;
public int friendRights;
public int otherRights;
public CefBrowser browser;
public ArrayList<ItemStack> upgrades;
public boolean doTurnOnAnim;
public long turnOnTime;
public Player laserUser;
public final Vector2i lastMousePos = new Vector2i();
public NibbleArray redstoneStatus; //null on client
public boolean autoVolume = true;
public int mouseType;
public static Screen deserialize(CompoundTag tag) {
Screen ret = new Screen();
ret.side = BlockSide.values()[tag.getByte("Side")];
ret.size = new Vector2i(tag.getInt("Width"), tag.getInt("Height"));
ret.resolution = new Vector2i(tag.getInt("ResolutionX"), tag.getInt("ResolutionY"));
ret.rotation = Rotation.values()[tag.getByte("Rotation")];
ret.url = tag.getString("URL");
ret.videoType = VideoType.getTypeFromURL(ret.url);
if (ret.resolution.x <= 0 || ret.resolution.y <= 0) {
float psx = ((float) ret.size.x) * 16.f - 4.f;
float psy = ((float) ret.size.y) * 16.f - 4.f;
psx *= 8.f; //TODO: Use ratio in config file
psy *= 8.f;
ret.resolution.x = (int) psx;
ret.resolution.y = (int) psy;
}
if (tag.contains("OwnerName")) {
String name = tag.getString("OwnerName");
UUID uuid = tag.getUUID("OwnerUUID");
ret.owner = new NameUUIDPair(name, uuid);
}
ListTag friends = tag.getList("Friends", 10);
ret.friends = new ArrayList<>(friends.size());
for (int i = 0; i < friends.size(); i++) {
CompoundTag nf = friends.getCompound(i);
NameUUIDPair pair = new NameUUIDPair(nf.getString("Name"), nf.getUUID("UUID"));
ret.friends.add(pair);
}
ret.friendRights = tag.getByte("FriendRights");
ret.otherRights = tag.getByte("OtherRights");
ListTag upgrades = tag.getList("Upgrades", 10);
ret.upgrades = new ArrayList<>();
for (int i = 0; i < upgrades.size(); i++)
ret.upgrades.add(ItemStack.of(upgrades.getCompound(i)));
if (tag.contains("AutoVolume"))
ret.autoVolume = tag.getBoolean("AutoVolume");
return ret;
}
public CompoundTag serialize() {
CompoundTag tag = new CompoundTag();
tag.putByte("Side", (byte) side.ordinal());
tag.putInt("Width", size.x);
tag.putInt("Height", size.y);
tag.putInt("ResolutionX", resolution.x);
tag.putInt("ResolutionY", resolution.y);
tag.putByte("Rotation", (byte) rotation.ordinal());
tag.putString("URL", url);
if (owner == null)
Log.warning("Found TES with NO OWNER!!");
else {
tag.putString("OwnerName", owner.name);
tag.putUUID("OwnerUUID", owner.uuid);
}
ListTag list = new ListTag();
for (NameUUIDPair f : friends) {
CompoundTag nf = new CompoundTag();
nf.putString("Name", f.name);
nf.putUUID("UUID", f.uuid);
list.add(nf);
}
tag.put("Friends", list);
tag.putByte("FriendRights", (byte) friendRights);
tag.putByte("OtherRights", (byte) otherRights);
list = new ListTag();
for (ItemStack is : upgrades)
list.add(is.save(new CompoundTag()));
tag.put("Upgrades", list);
tag.putBoolean("AutoVolume", autoVolume);
return tag;
}
public int rightsFor(Player ply) {
return rightsFor(ply.getGameProfile().getId());
}
public int rightsFor(UUID uuid) {
if (owner.uuid.equals(uuid))
return ScreenRights.ALL;
return friends.stream().anyMatch(f -> f.uuid.equals(uuid)) ? friendRights : otherRights;
}
public void setupRedstoneStatus(Level world, BlockPos start) {
if (world.isClientSide()) {
Log.warning("Called Screen.setupRedstoneStatus() on client.");
return;
}
if (redstoneStatus != null) {
Log.warning("Called Screen.setupRedstoneStatus() on server, but redstone status is non-null");
return;
}
Direction[] VALUES = Direction.values();
redstoneStatus = new NibbleArray(size.x * size.y);
final Direction facing = VALUES[side.reverse().ordinal()];
final ScreenIterator it = new ScreenIterator(start, side, size);
// TODO: cache chunk
while (it.hasNext()) {
int idx = it.getIndex();
redstoneStatus.set(idx, world.getSignal(it.next(), facing));
}
}
public void clampResolution() {
if (resolution.x > CommonConfig.Screen.maxResolutionX) {
float newY = ((float) resolution.y) * ((float) CommonConfig.Screen.maxResolutionX) / ((float) resolution.x);
resolution.x = CommonConfig.Screen.maxResolutionX;
resolution.y = (int) newY;
}
if (resolution.y > CommonConfig.Screen.maxResolutionY) {
float newX = ((float) resolution.x) * ((float) CommonConfig.Screen.maxResolutionY) / ((float) resolution.y);
resolution.x = (int) newX;
resolution.y = CommonConfig.Screen.maxResolutionY;
}
}
public void createBrowser(boolean doAnim) {
if (WebDisplays.PROXY instanceof ClientProxy clientProxy) {
browser = MCEF.createBrowser(WebDisplays.applyBlacklist(url != null ? url : "https://www.google.com"), false);
if (browser instanceof MCEFBrowser mcefBrowser) {
if (rotation.isVertical)
mcefBrowser.resize(resolution.y, resolution.x);
else
mcefBrowser.resize(resolution.x, resolution.y);
// uh yes this is intentional
// basically: on my laptop, this line caused an error inexplicably
// reason: the compiler didn't update this file, so it stayed as a Consumer<Integer> in the bytecode
//noinspection RedundantCast
mcefBrowser.setCursorChangeListener((MCEFCursorChangeListener) (type) -> mouseType = type);
}
doTurnOnAnim = doAnim;
turnOnTime = System.currentTimeMillis();
}
}
}
public void forEachScreenBlocks(BlockSide side, Consumer<BlockPos> func) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr != null) {
ScreenIterator it = new ScreenIterator(getBlockPos(), side, scr.size);
@ -261,7 +82,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
}
private final ArrayList<Screen> screens = new ArrayList<>();
private final ArrayList<ScreenData> screens = new ArrayList<>();
private net.minecraft.world.phys.AABB renderBB = new net.minecraft.world.phys.AABB(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
private boolean loaded = true;
public float ytVolume = Float.POSITIVE_INFINITY;
@ -275,7 +96,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public void unload() {
for (Screen scr : screens) {
for (ScreenData scr : screens) {
if (scr.browser != null) {
scr.browser.close(true);
scr.browser = null;
@ -295,7 +116,7 @@ public class ScreenBlockEntity extends BlockEntity {
return;
// very important to close these
for (Screen screen : screens) {
for (ScreenData screen : screens) {
if (screen.browser != null) {
screen.browser.close(true);
screen.browser = null;
@ -304,7 +125,7 @@ public class ScreenBlockEntity extends BlockEntity {
screens.clear();
for (int i = 0; i < list.size(); i++)
screens.add(Screen.deserialize(list.getCompound(i)));
screens.add(ScreenData.deserialize(list.getCompound(i)));
}
@Override
@ -317,8 +138,8 @@ public class ScreenBlockEntity extends BlockEntity {
@Override
public void handleUpdateTag(CompoundTag tag) {
load(tag);
for (Screen screen : screens) {
if (screen.browser == null) screen.createBrowser(false);
for (ScreenData screen : screens) {
if (screen.browser == null) screen.createBrowser(this, false);
if (screen.browser != null) screen.browser.loadURL(screen.url);
}
updateAABB();
@ -329,19 +150,19 @@ public class ScreenBlockEntity extends BlockEntity {
super.saveAdditional(tag);
ListTag list = new ListTag();
for (Screen scr : screens)
for (ScreenData scr : screens)
list.add(scr.serialize());
tag.put("WDScreens", list);
}
public Screen addScreen(BlockSide side, Vector2i size, @Nullable Vector2i resolution, @Nullable Player owner, boolean sendUpdate) {
for (Screen scr : screens) {
public ScreenData addScreen(BlockSide side, Vector2i size, @Nullable Vector2i resolution, @Nullable Player owner, boolean sendUpdate) {
for (ScreenData scr : screens) {
if (scr.side == side)
return scr;
}
Screen ret = new Screen();
ScreenData ret = new ScreenData();
ret.side = side;
ret.size = size;
ret.url = CommonConfig.Browser.homepage;
@ -398,8 +219,8 @@ public class ScreenBlockEntity extends BlockEntity {
return ret;
}
public Screen getScreen(BlockSide side) {
for (Screen scr : screens) {
public ScreenData getScreen(BlockSide side) {
for (ScreenData scr : screens) {
if (scr.side == side)
return scr;
}
@ -411,13 +232,13 @@ public class ScreenBlockEntity extends BlockEntity {
return screens.size();
}
public Screen getScreen(int idx) {
public ScreenData getScreen(int idx) {
return screens.get(idx);
}
public void clear() {
// very important that these get closed
for (Screen screen : screens)
for (ScreenData screen : screens)
if (screen.browser != null) {
screen.browser.close(true);
screen.browser = null;
@ -429,7 +250,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public static String url(String url) throws IOException {
System.out.println("URL received: " + url);
Log.info("URL received: " + url);
if (!(WebDisplays.PROXY instanceof ClientProxy)) {
List<ServerPlayer> serverPlayers = WebDisplays.PROXY.getServer().getPlayerList().getPlayers();
SyncPlugin.syncPlayers(serverPlayers);
@ -443,7 +264,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public void setScreenURL(BlockSide side, String url) throws IOException {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Attempt to change URL of non-existing screen on side %s", side.toString());
return;
@ -503,7 +324,7 @@ public class ScreenBlockEntity extends BlockEntity {
return;
}
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Tried to change resolution of non-existing screen on side %s", side.toString());
return;
@ -525,7 +346,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
}
private static Player getLaserUser(Screen scr) {
private static Player getLaserUser(ScreenData scr) {
if (scr.laserUser != null) {
if (scr.laserUser.isRemoved() || !scr.laserUser.getItemInHand(InteractionHand.MAIN_HAND).getItem().equals(ItemRegistry.LASER_POINTER.get()))
scr.laserUser = null;
@ -534,20 +355,20 @@ public class ScreenBlockEntity extends BlockEntity {
return scr.laserUser;
}
private static void checkLaserUserRights(Screen scr) {
private static void checkLaserUserRights(ScreenData scr) {
if (scr.laserUser != null && (scr.rightsFor(scr.laserUser) & ScreenRights.INTERACT) == 0)
scr.laserUser = null;
}
public void clearLaserUser(BlockSide side) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr != null)
scr.laserUser = null;
}
public void click(BlockSide side, Vector2i vec) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Attempt click non-existing screen of side %s", side.toString());
return;
@ -562,7 +383,7 @@ public class ScreenBlockEntity extends BlockEntity {
public void handleMouseEvent(BlockSide side, ClickControl.ControlType event, @Nullable Vector2i vec, int button) {
if (button > 1) return; // buttons above 1 crash the game
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Attempt inject mouse events on non-existing screen of side %s", side.toString());
return;
@ -701,7 +522,7 @@ public class ScreenBlockEntity extends BlockEntity {
if (level.isClientSide) {
WebDisplays.PROXY.trackScreen(this, false);
for (Screen scr : screens) {
for (ScreenData scr : screens) {
if (scr.browser != null) {
scr.browser.close(true);
scr.browser = null;
@ -714,7 +535,7 @@ public class ScreenBlockEntity extends BlockEntity {
Vector3i origin = new Vector3i(getBlockPos());
MutableAABB box = null;
for (Screen scr : screens) {
for (ScreenData scr : screens) {
Vector3i f = scr.side.forward;
int fx = Math.max(f.x, 0);
@ -797,7 +618,7 @@ public class ScreenBlockEntity extends BlockEntity {
// }
public void updateClientSideURL(CefBrowser target, String url) {
for (Screen scr : screens) {
for (ScreenData scr : screens) {
if (scr.browser == target) {
// TODO: what? lol
String webUrl;
@ -829,7 +650,7 @@ public class ScreenBlockEntity extends BlockEntity {
public void addFriend(ServerPlayer ply, BlockSide side, NameUUIDPair pair) {
if (!level.isClientSide) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Tried to add friend to invalid screen side %s", side.toString());
return;
@ -845,7 +666,7 @@ public class ScreenBlockEntity extends BlockEntity {
public void removeFriend(ServerPlayer ply, BlockSide side, NameUUIDPair pair) {
if (!level.isClientSide) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Tried to remove friend from invalid screen side %s", side.toString());
return;
@ -861,7 +682,7 @@ public class ScreenBlockEntity extends BlockEntity {
public void setRights(ServerPlayer ply, BlockSide side, int fr, int or) {
if (!level.isClientSide) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Tried to change rights of invalid screen on side %s", side.toString());
return;
@ -881,7 +702,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public void type(BlockSide side, String text, BlockPos soundPos, @Nullable ServerPlayer sender) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Tried to type on invalid screen on side %s", side.toString());
return;
@ -976,7 +797,7 @@ public class ScreenBlockEntity extends BlockEntity {
public boolean addUpgrade(BlockSide side, ItemStack is, @Nullable Player player, boolean abortIfExisting) {
if (level.isClientSide) {
IUpgrade itemAsUpgrade = (IUpgrade) is.getItem();
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
// if (abortIfExisting && scr.upgrades.stream().anyMatch(otherStack -> itemAsUpgrade.isSameUpgrade(is, otherStack)))
// return false; //Upgrade already exists
ItemStack isCopy = is.copy(); //FIXME: Duct tape fix, because the original stack will be shrinked
@ -985,7 +806,7 @@ public class ScreenBlockEntity extends BlockEntity {
return false;
}
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Tried to add an upgrade on invalid screen on side %s", side.toString());
return false;
@ -1019,7 +840,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public boolean hasUpgrade(BlockSide side, ItemStack is) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null)
return false;
@ -1031,7 +852,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public boolean hasUpgrade(BlockSide side, DefaultUpgrade du) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (du == DefaultUpgrade.LASERMOUSE) {
return scr != null && scr.upgrades.stream().anyMatch(du::matchesLaserMouse);
} else if (du == DefaultUpgrade.REDINPUT) {
@ -1049,7 +870,7 @@ public class ScreenBlockEntity extends BlockEntity {
if (level.isClientSide)
return;
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Tried to remove an upgrade on invalid screen on side %s", side.toString());
return;
@ -1102,11 +923,11 @@ public class ScreenBlockEntity extends BlockEntity {
}
}
private Screen getScreenForLaserOp(BlockSide side, Player ply) {
private ScreenData getScreenForLaserOp(BlockSide side, Player ply) {
if (level.isClientSide)
return null;
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Called laser operation on invalid screen on side %s", side.toString());
return null;
@ -1124,7 +945,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public void laserDownMove(BlockSide side, Player ply, Vector2i pos, boolean down, int button) {
Screen scr = getScreenForLaserOp(side, ply);
ScreenData scr = getScreenForLaserOp(side, ply);
if (scr != null) {
if (button == -1)
@ -1137,7 +958,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public void laserUp(BlockSide side, Player ply, int button) {
Screen scr = getScreenForLaserOp(side, ply);
ScreenData scr = getScreenForLaserOp(side, ply);
if (scr != null) {
if (getLaserUser(scr) == ply) {
@ -1148,7 +969,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public void onDestroy(@Nullable Player ply) {
for (Screen scr : screens) {
for (ScreenData scr : screens) {
scr.upgrades.forEach(is -> dropUpgrade(is, scr.side, ply));
scr.upgrades.clear();
}
@ -1157,8 +978,8 @@ public class ScreenBlockEntity extends BlockEntity {
}
public void disableScreen(BlockSide side) {
Screen remove = null;
for (Screen screen : screens) {
ScreenData remove = null;
for (ScreenData screen : screens) {
if (screen.side == side) {
remove = screen;
break;
@ -1168,7 +989,7 @@ public class ScreenBlockEntity extends BlockEntity {
if (remove == null) return;
if (level != null && !level.isClientSide) {
final Screen scrn = remove;
final ScreenData scrn = remove;
remove.upgrades.forEach(is -> dropUpgrade(is, scrn.side, null));
}
@ -1189,7 +1010,7 @@ public class ScreenBlockEntity extends BlockEntity {
return;
}
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Called TileEntityScreen.setOwner() on invalid screen on side %s", side.toString());
return;
@ -1202,7 +1023,7 @@ public class ScreenBlockEntity extends BlockEntity {
}
public void setRotation(BlockSide side, Rotation rot) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Trying to change rotation of invalid screen on side %s", side.toString());
return;
@ -1240,7 +1061,7 @@ public class ScreenBlockEntity extends BlockEntity {
// }
public void setAutoVolume(BlockSide side, boolean av) {
Screen scr = getScreen(side);
ScreenData scr = getScreen(side);
if (scr == null) {
Log.error("Trying to toggle auto-volume on invalid screen (side %s)", side.toString());
return;
@ -1256,6 +1077,72 @@ public class ScreenBlockEntity extends BlockEntity {
}
}
public void deactivate() {
for (ScreenData screen : screens) {
if (screen.browser != null) {
screen.browser.close(true);
screen.browser = null;
}
}
}
public void activate() {
for (ScreenData screen : screens) {
if (screen.browser == null)
screen.createBrowser(this, false);
}
}
public void interact(BlockHitResult result, Consumer<Vector2i> func) {
BlockState state = getBlockState();
if (state.getBlock() instanceof ScreenBlock) {
Vector3i pos = new Vector3i(result.getBlockPos());
BlockSide side = BlockSide.values()[result.getDirection().ordinal()];
Multiblock.findOrigin(Minecraft.getInstance().level, pos, side, null);
//Since rights aren't synchronized, let the server check them for us...
ScreenData scr = this.getScreen(side);
if (scr.browser != null) {
float hitX = ((float) result.getLocation().x) - (float) pos.x;
float hitY = ((float) result.getLocation().y) - (float) pos.y;
float hitZ = ((float) result.getLocation().z) - (float) pos.z;
Vector2i tmp = new Vector2i();
if (ScreenBlock.hit2pixels(side, result.getBlockPos(), new Vector3i(result.getBlockPos()), scr, hitX, hitY, hitZ, tmp)) {
func.accept(tmp);
}
}
}
}
public BlockHitResult trace(BlockSide side, Vec3 start, Vec3 look) {
AABB box = getRenderBoundingBox();
double pHitDistance = box.distanceToSqr(start) + 2;
Vec3 vec32 = start.add(look.x * pHitDistance, look.y * pHitDistance, look.z * pHitDistance);
box = box.move(
-getBlockPos().getX(),
-getBlockPos().getY(),
-getBlockPos().getZ()
);
BlockHitResult bhr = AABB.clip(Arrays.asList(box), start, vec32, getBlockPos());
if (bhr == null || bhr.getType() != HitResult.Type.BLOCK || bhr.getDirection().ordinal() != side.ordinal()) {
bhr = AABB.clip(Arrays.asList(box), vec32, start, getBlockPos());
if (bhr == null || bhr.getType() != HitResult.Type.BLOCK || bhr.getDirection().ordinal() != side.ordinal()) {
return BlockHitResult.miss(
vec32,
bhr == null ? Direction.getNearest(look.x, look.y, look.z).getOpposite() : bhr.getDirection(),
getBlockPos()
);
}
}
return bhr;
}
// @Override
// public boolean shouldRefresh(Level world, BlockPos pos, @Nonnull BlockState oldState, @Nonnull BlockState newState) {

View File

@ -0,0 +1,211 @@
package net.montoyo.wd.entity;
import com.cinemamod.mcef.MCEF;
import com.cinemamod.mcef.MCEFBrowser;
import com.cinemamod.mcef.listeners.MCEFCursorChangeListener;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.client.ClientProxy;
import net.montoyo.wd.config.CommonConfig;
import net.montoyo.wd.core.ScreenRights;
import net.montoyo.wd.utilities.*;
import net.montoyo.wd.utilities.browser.InWorldQueries;
import net.montoyo.wd.utilities.browser.WDBrowser;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.data.Rotation;
import net.montoyo.wd.utilities.math.Vector2i;
import net.montoyo.wd.utilities.serialization.NameUUIDPair;
import org.cef.browser.CefBrowser;
import java.util.ArrayList;
import java.util.UUID;
public class ScreenData {
public BlockSide side;
public Vector2i size;
public Vector2i resolution;
public Rotation rotation = Rotation.ROT_0;
public String url;
protected VideoType videoType;
public NameUUIDPair owner;
public ArrayList<NameUUIDPair> friends;
public int friendRights;
public int otherRights;
public CefBrowser browser;
public ArrayList<ItemStack> upgrades;
public boolean doTurnOnAnim;
public long turnOnTime;
public Player laserUser;
public final Vector2i lastMousePos = new Vector2i();
public NibbleArray redstoneStatus; //null on client
public boolean autoVolume = true;
public int mouseType;
public static ScreenData deserialize(CompoundTag tag) {
ScreenData ret = new ScreenData();
ret.side = BlockSide.values()[tag.getByte("Side")];
ret.size = new Vector2i(tag.getInt("Width"), tag.getInt("Height"));
ret.resolution = new Vector2i(tag.getInt("ResolutionX"), tag.getInt("ResolutionY"));
ret.rotation = Rotation.values()[tag.getByte("Rotation")];
ret.url = tag.getString("URL");
ret.videoType = VideoType.getTypeFromURL(ret.url);
if (ret.resolution.x <= 0 || ret.resolution.y <= 0) {
float psx = ((float) ret.size.x) * 16.f - 4.f;
float psy = ((float) ret.size.y) * 16.f - 4.f;
psx *= 8.f; //TODO: Use ratio in config file
psy *= 8.f;
ret.resolution.x = (int) psx;
ret.resolution.y = (int) psy;
}
if (tag.contains("OwnerName")) {
String name = tag.getString("OwnerName");
UUID uuid = tag.getUUID("OwnerUUID");
ret.owner = new NameUUIDPair(name, uuid);
}
ListTag friends = tag.getList("Friends", 10);
ret.friends = new ArrayList<>(friends.size());
for (int i = 0; i < friends.size(); i++) {
CompoundTag nf = friends.getCompound(i);
NameUUIDPair pair = new NameUUIDPair(nf.getString("Name"), nf.getUUID("UUID"));
ret.friends.add(pair);
}
ret.friendRights = tag.getByte("FriendRights");
ret.otherRights = tag.getByte("OtherRights");
ListTag upgrades = tag.getList("Upgrades", 10);
ret.upgrades = new ArrayList<>();
for (int i = 0; i < upgrades.size(); i++)
ret.upgrades.add(ItemStack.of(upgrades.getCompound(i)));
if (tag.contains("AutoVolume"))
ret.autoVolume = tag.getBoolean("AutoVolume");
return ret;
}
public CompoundTag serialize() {
CompoundTag tag = new CompoundTag();
tag.putByte("Side", (byte) side.ordinal());
tag.putInt("Width", size.x);
tag.putInt("Height", size.y);
tag.putInt("ResolutionX", resolution.x);
tag.putInt("ResolutionY", resolution.y);
tag.putByte("Rotation", (byte) rotation.ordinal());
tag.putString("URL", url);
if (owner == null)
Log.warning("Found TES with NO OWNER!!");
else {
tag.putString("OwnerName", owner.name);
tag.putUUID("OwnerUUID", owner.uuid);
}
ListTag list = new ListTag();
for (NameUUIDPair f : friends) {
CompoundTag nf = new CompoundTag();
nf.putString("Name", f.name);
nf.putUUID("UUID", f.uuid);
list.add(nf);
}
tag.put("Friends", list);
tag.putByte("FriendRights", (byte) friendRights);
tag.putByte("OtherRights", (byte) otherRights);
list = new ListTag();
for (ItemStack is : upgrades)
list.add(is.save(new CompoundTag()));
tag.put("Upgrades", list);
tag.putBoolean("AutoVolume", autoVolume);
return tag;
}
public int rightsFor(Player ply) {
return rightsFor(ply.getGameProfile().getId());
}
public int rightsFor(UUID uuid) {
if (owner.uuid.equals(uuid))
return ScreenRights.ALL;
return friends.stream().anyMatch(f -> f.uuid.equals(uuid)) ? friendRights : otherRights;
}
public void setupRedstoneStatus(Level world, BlockPos start) {
if (world.isClientSide()) {
Log.warning("Called Screen.setupRedstoneStatus() on client.");
return;
}
if (redstoneStatus != null) {
Log.warning("Called Screen.setupRedstoneStatus() on server, but redstone status is non-null");
return;
}
Direction[] VALUES = Direction.values();
redstoneStatus = new NibbleArray(size.x * size.y);
final Direction facing = VALUES[side.reverse().ordinal()];
final ScreenIterator it = new ScreenIterator(start, side, size);
while (it.hasNext()) {
int idx = it.getIndex();
redstoneStatus.set(idx, world.getSignal(it.next(), facing));
}
}
public void clampResolution() {
if (resolution.x > CommonConfig.Screen.maxResolutionX) {
float newY = ((float) resolution.y) * ((float) CommonConfig.Screen.maxResolutionX) / ((float) resolution.x);
resolution.x = CommonConfig.Screen.maxResolutionX;
resolution.y = (int) newY;
}
if (resolution.y > CommonConfig.Screen.maxResolutionY) {
float newX = ((float) resolution.x) * ((float) CommonConfig.Screen.maxResolutionY) / ((float) resolution.y);
resolution.x = (int) newX;
resolution.y = CommonConfig.Screen.maxResolutionY;
}
}
public void createBrowser(ScreenBlockEntity be, boolean doAnim) {
if (WebDisplays.PROXY instanceof ClientProxy) {
browser = WDBrowser.createBrowser(WebDisplays.applyBlacklist(url != null ? url : "https://www.google.com"), false);
// set screen
if (browser instanceof MCEFBrowser mcefBrowser) {
if (rotation.isVertical)
mcefBrowser.resize(resolution.y, resolution.x);
else
mcefBrowser.resize(resolution.x, resolution.y);
mcefBrowser.setCursorChangeListener((type) -> mouseType = type);
}
// setup screen as in world
if (browser instanceof WDBrowser wdBrowser) {
InWorldQueries.attach(be, side, wdBrowser);
}
doTurnOnAnim = doAnim;
turnOnTime = System.currentTimeMillis();
}
}
}

View File

@ -17,11 +17,12 @@ import net.montoyo.wd.config.ClientConfig;
import net.montoyo.wd.controls.builtin.ClickControl;
import net.montoyo.wd.core.DefaultUpgrade;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.registry.BlockRegistry;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.net.WDNetworkRegistry;
import net.montoyo.wd.net.server_bound.C2SMessageScreenCtrl;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.registry.BlockRegistry;
import net.montoyo.wd.utilities.Multiblock;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.math.Vector2i;
import net.montoyo.wd.utilities.math.Vector3i;
@ -61,7 +62,7 @@ public class ItemLaserPointer extends Item implements WDItem {
if (te != null && te.hasUpgrade(side, DefaultUpgrade.LASERMOUSE)) { //hasUpgrade returns false is there's no screen on side 'side'
//Since rights aren't synchronized, let the server check them for us...
ScreenBlockEntity.Screen scr = te.getScreen(side);
ScreenData scr = te.getScreen(side);
if (scr.browser != null) {
float hitX = ((float) result.getLocation().x) - (float) pos.x;
@ -81,7 +82,7 @@ public class ItemLaserPointer extends Item implements WDItem {
deselectScreen();
}
private static void laserClick(ScreenBlockEntity tes, BlockSide side, ScreenBlockEntity.Screen scr, Vector2i hit) {
private static void laserClick(ScreenBlockEntity tes, BlockSide side, ScreenData scr, Vector2i hit) {
tes.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
if (pointedScreen == tes && pointedScreenSide == side) {
long t = System.currentTimeMillis();
@ -105,7 +106,7 @@ public class ItemLaserPointer extends Item implements WDItem {
}
public static void press(boolean press, int button) {
if (button <= 1 && ClientConfig.switchButtons)
if (button <= 1 && ClientConfig.Input.switchButtons)
button = 1 - button;
if (button == 0) left = press;
@ -119,11 +120,6 @@ public class ItemLaserPointer extends Item implements WDItem {
BlockSide side = BlockSide.values()[result.getDirection().ordinal()];
Multiblock.findOrigin(mc.level, pos, side, null);
float hitX = ((float) result.getLocation().x) - (float) pos.x;
float hitY = ((float) result.getLocation().y) - (float) pos.y;
float hitZ = ((float) result.getLocation().z) - (float) pos.z;
Vector2i tmp = new Vector2i();
BlockEntity be = mc.level.getBlockEntity(pos.toBlock());
if (!(be instanceof ScreenBlockEntity)) return;
@ -131,20 +127,16 @@ public class ItemLaserPointer extends Item implements WDItem {
ScreenBlockEntity te = (ScreenBlockEntity) be;
if (te.hasUpgrade(side, DefaultUpgrade.LASERMOUSE)) { //hasUpgrade returns false is there's no screen on side 'side'
//Since rights aren't synchronized, let the server check them for us...
ScreenBlockEntity.Screen scr = te.getScreen(side);
if (scr.browser != null) {
if (ScreenBlock.hit2pixels(side, result.getBlockPos(), new Vector3i(result.getBlockPos()), scr, hitX, hitY, hitZ, tmp)) {
te.handleMouseEvent(side, ClickControl.ControlType.MOVE, tmp, -1);
te.handleMouseEvent(side, press ? ClickControl.ControlType.DOWN : ClickControl.ControlType.UP, tmp, button);
if (press)
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserDown(te, side, tmp, button));
else
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserUp(te, side, button));
}
}
int finalButton = button;
te.interact(result, (hit) -> {
te.handleMouseEvent(side, ClickControl.ControlType.MOVE, hit, -1);
te.handleMouseEvent(side, press ? ClickControl.ControlType.DOWN : ClickControl.ControlType.UP, hit, finalButton);
if (press)
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserDown(te, side, hit, finalButton));
else
WDNetworkRegistry.INSTANCE.sendToServer(C2SMessageScreenCtrl.laserUp(te, side, finalButton));
});
}
}

View File

@ -17,6 +17,7 @@ import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.block.ScreenBlock;
import net.montoyo.wd.core.IPeripheral;
import net.montoyo.wd.core.ScreenRights;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.Multiblock;
@ -95,8 +96,8 @@ public class ItemLinker extends Item implements WDItem {
return InteractionResult.SUCCESS;
}
ScreenBlockEntity.Screen scr = ((ScreenBlockEntity) te).getScreen(side);
if (scr == null)
ScreenData scr = ((ScreenBlockEntity) te).getScreen(side);
if(scr == null)
Util.toast(context.getPlayer(), "turnOn");
else if ((scr.rightsFor(context.getPlayer()) & ScreenRights.MANAGE_UPGRADES) == 0)
Util.toast(context.getPlayer(), "restrictions");

View File

@ -14,6 +14,7 @@ import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.montoyo.wd.block.ScreenBlock;
import net.montoyo.wd.config.CommonConfig;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.utilities.*;
import net.montoyo.wd.utilities.math.Vector3i;
@ -61,8 +62,8 @@ public class ItemOwnershipThief extends Item implements WDItem {
return InteractionResult.SUCCESS;
ScreenBlockEntity tes = (ScreenBlockEntity) te;
ScreenBlockEntity.Screen scr = tes.getScreen(side);
if (scr == null)
ScreenData scr = tes.getScreen(side);
if(scr == null)
return InteractionResult.SUCCESS;
Log.warning("Owner of screen at %d %d %d, side %s was changed from %s (UUID %s) to %s (UUID %s)", bp.getX(), bp.getY(), bp.getZ(), side.toString(), scr.owner.name, scr.owner.uuid.toString(), context.getPlayer().getName(), context.getPlayer().getGameProfile().getId().toString());
@ -77,7 +78,7 @@ public class ItemOwnershipThief extends Item implements WDItem {
return InteractionResult.SUCCESS;
Vector3i pos = new Vector3i(context.getClickedPos());
BlockSide side = BlockSide.values()[context.getHorizontalDirection().ordinal()];
BlockSide side = BlockSide.values()[context.getClickedFace().ordinal()];
Multiblock.findOrigin(context.getLevel(), pos, side, null);
BlockEntity te = context.getLevel().getBlockEntity(pos.toBlock());

View File

@ -12,6 +12,7 @@ import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.montoyo.wd.block.ScreenBlock;
import net.montoyo.wd.data.ScreenConfigData;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.utilities.data.BlockSide;
import net.montoyo.wd.utilities.Multiblock;
@ -36,7 +37,7 @@ public class ItemScreenConfigurator extends Item implements WDItem {
return InteractionResult.SUCCESS;
Vector3i origin = new Vector3i(context.getClickedPos());
BlockSide side = BlockSide.values()[context.getHorizontalDirection().getOpposite().ordinal()];
BlockSide side = BlockSide.values()[context.getClickedFace().ordinal()];
Multiblock.findOrigin(context.getLevel(), origin, side, null);
BlockEntity te = context.getLevel().getBlockEntity(origin.toBlock());
@ -46,8 +47,8 @@ public class ItemScreenConfigurator extends Item implements WDItem {
return InteractionResult.SUCCESS;
}
ScreenBlockEntity.Screen scr = ((ScreenBlockEntity) te).getScreen(side);
if (scr == null)
ScreenData scr = ((ScreenBlockEntity) te).getScreen(side);
if(scr == null)
Util.toast(context.getPlayer(), "turnOn");
else
(new ScreenConfigData(origin, side, scr)).sendTo((ServerPlayer) context.getPlayer());

View File

@ -51,7 +51,7 @@ public class ClientTaskCheckFile extends ClientTask<ClientTaskCheckFile> {
public String getURL() {
try {
return ((new StringBuilder("wd://"))).append(uuid.toString()).append('/').append(URLEncoder.encode(fname, "UTF-8")).toString();
return ((new StringBuilder("webdisplays://"))).append(uuid.toString()).append('/').append(URLEncoder.encode(fname, "UTF-8")).toString();
} catch(UnsupportedEncodingException ex) {
ex.printStackTrace();
return "hi";

View File

@ -10,6 +10,7 @@ import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraftforge.network.NetworkEvent;
import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.entity.ScreenData;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.net.Packet;
import net.montoyo.wd.utilities.*;
@ -27,24 +28,24 @@ import static net.montoyo.wd.block.ScreenBlock.hasTE;
public class S2CMessageAddScreen extends Packet {
private boolean clear;
private Vector3i pos;
private ScreenBlockEntity.Screen[] screens;
private ScreenData[] screens;
public S2CMessageAddScreen(ScreenBlockEntity tes) {
clear = true;
pos = new Vector3i(tes.getBlockPos());
screens = new ScreenBlockEntity.Screen[tes.screenCount()];
screens = new ScreenData[tes.screenCount()];
for (int i = 0; i < tes.screenCount(); i++)
screens[i] = tes.getScreen(i);
}
public S2CMessageAddScreen(ScreenBlockEntity tes, ScreenBlockEntity.Screen... toSend) {
public S2CMessageAddScreen(ScreenBlockEntity tes, ScreenData... toSend) {
clear = false;
pos = new Vector3i(tes.getBlockPos());
screens = toSend;
}
public S2CMessageAddScreen(boolean clear, Vector3i pos, ScreenBlockEntity.Screen[] screens) {
public S2CMessageAddScreen(boolean clear, Vector3i pos, ScreenData[] screens) {
this.clear = clear;
this.pos = pos;
this.screens = screens;
@ -58,9 +59,9 @@ public class S2CMessageAddScreen extends Packet {
int cnt = buf.readByte() & 7;
screens = new ScreenBlockEntity.Screen[cnt];
screens = new ScreenData[cnt];
for (int i = 0; i < cnt; i++) {
screens[i] = new ScreenBlockEntity.Screen();
screens[i] = new ScreenData();
screens[i].side = BlockSide.values()[buf.readByte()];
screens[i].size = new Vector2i(buf);
screens[i].url = buf.readUtf();
@ -81,7 +82,7 @@ public class S2CMessageAddScreen extends Packet {
pos.writeTo(buf);
buf.writeByte(screens.length);
for (ScreenBlockEntity.Screen scr : screens) {
for (ScreenData scr : screens) {
buf.writeByte(scr.side.ordinal());
scr.size.writeTo(buf);
buf.writeUtf(scr.url);
@ -116,8 +117,8 @@ public class S2CMessageAddScreen extends Packet {
if (clear)
tes.clear();
for (ScreenBlockEntity.Screen entry : screens) {
ScreenBlockEntity.Screen scr = tes.addScreen(entry.side, entry.size, entry.resolution, null, false);
for (ScreenData entry : screens) {
ScreenData scr = tes.addScreen(entry.side, entry.size, entry.resolution, null, false);
scr.rotation = entry.rotation;
String webUrl;

View File

@ -4,31 +4,33 @@
package net.montoyo.wd.utilities;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import com.mojang.logging.LogUtils;
import org.slf4j.Logger;
public abstract class Log {
private static final Logger logger = LogUtils.getLogger();
public static void info(String what, Object... data) {
LogManager.getLogger("WebDisplays").log(Level.INFO, String.format(what, data));
logger.info(String.format(what, data));
}
public static void warning(String what, Object... data) {
LogManager.getLogger("WebDisplays").log(Level.WARN, String.format(what, data));
logger.warn(String.format(what, data));
}
public static void error(String what, Object... data) {
LogManager.getLogger("WebDisplays").log(Level.ERROR, String.format(what, data));
logger.error(String.format(what, data));
}
public static void infoEx(String what, Throwable e, Object... data) {
LogManager.getLogger("WebDisplays").log(Level.INFO, String.format(what, data), e);
logger.info(String.format(what, data), e);
}
public static void warningEx(String what, Throwable e, Object... data) {
LogManager.getLogger("WebDisplays").log(Level.WARN, String.format(what, data), e);
logger.warn(String.format(what, data), e);
}
public static void errorEx(String what, Throwable e, Object... data) {
LogManager.getLogger("WebDisplays").log(Level.ERROR, String.format(what, data), e);
logger.error(String.format(what, data), e);
}
}

View File

@ -0,0 +1,14 @@
package net.montoyo.wd.utilities.browser;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.utilities.browser.handlers.js.queries.GetSizeQuery;
import net.montoyo.wd.utilities.data.BlockSide;
public class InWorldQueries {
private static final GetSizeQuery getSize = new GetSizeQuery();
public static void attach(ScreenBlockEntity blockEntity, BlockSide side, WDBrowser browser) {
browser.setBe(blockEntity, side);
browser.queryHandlers().put(getSize.getName(), getSize);
}
}

View File

@ -0,0 +1,40 @@
package net.montoyo.wd.utilities.browser;
import com.cinemamod.mcef.MCEF;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.utilities.browser.handlers.js.queries.ElementCenterQuery;
import net.montoyo.wd.utilities.browser.handlers.js.JSQueryHandler;
import net.montoyo.wd.utilities.data.BlockSide;
import org.cef.browser.CefBrowser;
import java.util.HashMap;
import java.util.Map;
public interface WDBrowser {
static CefBrowser createBrowser(String url, boolean transparent) {
WDClientBrowser browser = new WDClientBrowser(MCEF.getClient(), url, transparent);
browser.setCloseAllowed();
browser.createImmediately();
registerQueries(browser);
return browser;
}
static void registerQueries(WDBrowser browser) {
Map<String, JSQueryHandler> handlerMap = browser.queryHandlers();
JSQueryHandler handler;
handler = browser.focusedElement();
handlerMap.put(handler.getName(), handler);
handler = browser.pointerLockElement();
handlerMap.put(handler.getName(), handler);
}
HashMap<String, JSQueryHandler> queryHandlers();
ElementCenterQuery focusedElement();
ElementCenterQuery pointerLockElement();
void setBe(ScreenBlockEntity blockEntity, BlockSide side);
ScreenBlockEntity getBe();
BlockSide getSide();
}

View File

@ -0,0 +1,57 @@
package net.montoyo.wd.utilities.browser;
import com.cinemamod.mcef.MCEFBrowser;
import com.cinemamod.mcef.MCEFClient;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.utilities.browser.handlers.js.queries.ElementCenterQuery;
import net.montoyo.wd.utilities.browser.handlers.js.JSQueryHandler;
import net.montoyo.wd.utilities.data.BlockSide;
import java.util.HashMap;
public class WDClientBrowser extends MCEFBrowser implements WDBrowser {
ElementCenterQuery focusedEl = new ElementCenterQuery("ActiveElement", "document.activeElement");
ElementCenterQuery pointerLockEl =
new ElementCenterQuery("PointerElement", "document.pointerLockElement")
.addAdditional("unadjust", "document.webdisplays__unadjustPointerMotion")
;
HashMap<String, JSQueryHandler> handlerHashMap = new HashMap<>();
ScreenBlockEntity be;
BlockSide side;
public WDClientBrowser(MCEFClient client, String url, boolean transparent) {
super(client, url, transparent);
}
@Override
public HashMap<String, JSQueryHandler> queryHandlers() {
return handlerHashMap;
}
@Override
public ElementCenterQuery focusedElement() {
return focusedEl;
}
@Override
public ElementCenterQuery pointerLockElement() {
return pointerLockEl;
}
@Override
public void setBe(ScreenBlockEntity blockEntity, BlockSide side) {
this.be = blockEntity;
this.side = side;
}
@Override
public ScreenBlockEntity getBe() {
return be;
}
@Override
public BlockSide getSide() {
return side;
}
}

View File

@ -0,0 +1,69 @@
package net.montoyo.wd.utilities.browser.handlers;
import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.client.ClientProxy;
import net.montoyo.wd.utilities.browser.handlers.js.Scripts;
import net.montoyo.wd.entity.ScreenBlockEntity;
import net.montoyo.wd.net.WDNetworkRegistry;
import net.montoyo.wd.net.server_bound.C2SMessageMinepadUrl;
import org.cef.CefSettings;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.handler.CefDisplayHandler;
public class DisplayHandler implements CefDisplayHandler {
public static final CefDisplayHandler INSTANCE = new DisplayHandler();
@Override
public void onAddressChange(CefBrowser browser, CefFrame cefFrame, String url) {
ClientProxy proxy = ((ClientProxy) WebDisplays.PROXY);
if (browser != null) {
long t = System.currentTimeMillis();
for (ClientProxy.PadData pd : proxy.getPads()) {
if (pd.view == browser && t - pd.lastSent() >= 1000) {
if (WebDisplays.isSiteBlacklisted(url))
pd.view.loadURL(WebDisplays.BLACKLIST_URL);
else {
pd.updateTime(); //Avoid spamming the server with porn URLs
WDNetworkRegistry.INSTANCE.sendToServer(new C2SMessageMinepadUrl(pd.id, url));
}
break;
}
}
for (ScreenBlockEntity tes : proxy.getScreens())
tes.updateClientSideURL(browser, url);
}
// enables a custom pointer lock api
browser.executeJavaScript(Scripts.POINTER_LOCK, "WebDisplays", 0);
}
@Override
public void onTitleChange(CefBrowser cefBrowser, String s) {
}
@Override
public boolean onTooltip(CefBrowser cefBrowser, String s) {
return false;
}
@Override
public void onStatusMessage(CefBrowser cefBrowser, String s) {
}
@Override
public boolean onConsoleMessage(CefBrowser cefBrowser, CefSettings.LogSeverity logSeverity, String s, String s1, int i) {
return false;
}
@Override
public boolean onCursorChange(CefBrowser cefBrowser, int i) {
return false;
}
}

View File

@ -0,0 +1,147 @@
package net.montoyo.wd.utilities.browser.handlers;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import net.montoyo.wd.utilities.browser.WDBrowser;
import net.montoyo.wd.utilities.browser.handlers.js.JSQueryHandler;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.callback.CefQueryCallback;
import org.cef.handler.CefMessageRouterHandlerAdapter;
import java.util.ArrayList;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
public class WDRouter extends CefMessageRouterHandlerAdapter {
public static final WDRouter INSTANCE = new WDRouter();
private static boolean exists = false;
public WDRouter() {
if (exists) throw new RuntimeException("Can only have one WD message router.");
exists = true;
}
class QueryData {
CefBrowser browser;
String type;
BiConsumer<String, CefQueryCallback> consumer;
public QueryData(CefBrowser browser, String type, BiConsumer<String, CefQueryCallback> consumer) {
this.browser = browser;
this.type = type;
this.consumer = consumer;
}
}
ArrayList<QueryData> awaitingQueries = new ArrayList<>();
@Override
public boolean onQuery(CefBrowser browser, CefFrame frame, long queryId, String request, boolean persistent, CefQueryCallback callback) {
if (request.startsWith("WebDisplays_")) {
request = request.substring("Webdisplays_".length());
QueryData target = null;
for (QueryData awaitingQuery : awaitingQueries) {
if (browser != awaitingQuery.browser) continue;
if (request.startsWith(awaitingQuery.type)) {
String requestData = request.substring(awaitingQuery.type.length());
target = awaitingQuery;
awaitingQuery.consumer.accept(requestData, callback);
break;
}
}
if (target != null) {
awaitingQueries.remove(target);
callback.success("");
} else {
if (browser instanceof WDBrowser wdBrowser) {
Map<String, JSQueryHandler> handlerMap = wdBrowser.queryHandlers();
int i0 = request.indexOf('('); // legacy, TODO: support
int i1 = request.indexOf('{');
if (i0 == -1) i0 = i1;
if (i1 == -1) i1 = i0;
if (i1 == -1) {
if (handlerMap.containsKey(request)) {
if (!handlerMap.get(request).handle(browser, frame, null, persistent, callback)) {
callback.failure(-1, "Query " + queryId + " with data " + request + " completed, but wasn't marked as successful.");
}
}
} else {
int min = Math.min(i0, i1);
String text = request.substring(0, min);
if (handlerMap.containsKey(text)) {
JsonObject obj = null;
if (request.charAt(min) == '{')
obj = gson.fromJson(request.substring(min), JsonObject.class);
if (!handlerMap.get(text).handle(browser, frame, obj, persistent, callback)) {
callback.failure(-1, "Query " + queryId + " with data " + request + " completed, but wasn't marked as successful.");
}
}
callback.failure(-1, "Query " + queryId + " with data " + request + " completed, but there was no active request waiting for the result.");
}
}
}
return true;
}
return false;
}
private static final Gson gson = new Gson();
public class Task<T> {
QueryData qd;
CompletableFuture<T> wrapped;
public Task(QueryData qd, CompletableFuture<T> wrapped) {
this.qd = qd;
this.wrapped = wrapped;
}
public void cancel() {
wrapped.cancel(true);
awaitingQueries.remove(qd);
}
public Task<T> thenAccept(Consumer<T> consumer) {
wrapped.thenAccept(consumer);
return this;
}
}
public Task<JsonObject> requestJson(CefBrowser screen, String queryType, String script) {
JsonObject[] obj = new JsonObject[1];
QueryData qd = new QueryData(
screen, queryType,
(data, context) -> {
obj[0] = gson.fromJson(data, JsonObject.class);
}
);
awaitingQueries.add(qd);
screen.executeJavaScript(script, "", 0);
return new Task<>(
qd,
CompletableFuture.supplyAsync(() -> {
while (obj[0] == null) {
try {
Thread.sleep(1);
} catch (Throwable ignored) {
}
}
return obj[0];
})
);
}
}

View File

@ -0,0 +1,9 @@
package net.montoyo.wd.utilities.browser.handlers.js;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(RetentionPolicy.RUNTIME)
public @interface FileName {
String value();
}

View File

@ -0,0 +1,20 @@
package net.montoyo.wd.utilities.browser.handlers.js;
import com.google.gson.JsonObject;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.callback.CefQueryCallback;
public abstract class JSQueryHandler {
protected final String name;
public JSQueryHandler(String name) {
this.name = name;
}
public abstract boolean handle(CefBrowser browser, CefFrame frame, JsonObject data, boolean persistent, CefQueryCallback callback);
public final String getName() {
return name;
}
}

View File

@ -0,0 +1,33 @@
package net.montoyo.wd.utilities.browser.handlers.js;
import net.montoyo.wd.utilities.browser.handlers.DisplayHandler;
import java.io.InputStream;
import java.lang.reflect.Field;
public class Scripts {
private static int index = 1;
@FileName("assets/webdisplays/js/pointer_lock.js")
public static final String POINTER_LOCK = get();
@FileName("assets/webdisplays/js/mouse_event.js")
public static final String MOUSE_EVENT = get();
@FileName("assets/webdisplays/js/query_element.js")
public static final String QUERY_ELEMENT = get();
private static String get() {
Field field = Scripts.class.getDeclaredFields()[index++];
FileName name = field.getAnnotation(FileName.class);
String text;
try {
InputStream is = DisplayHandler.class.getClassLoader().getResourceAsStream(name.value());
text = new String(is.readAllBytes());
is.close();
} catch (Throwable err) {
throw new RuntimeException(err);
}
return text;
}
}

View File

@ -0,0 +1,89 @@
package net.montoyo.wd.utilities.browser.handlers.js.queries;
import com.google.gson.JsonObject;
import net.montoyo.wd.utilities.browser.handlers.js.JSQueryHandler;
import net.montoyo.wd.utilities.browser.handlers.js.Scripts;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.callback.CefQueryCallback;
public class ElementCenterQuery extends JSQueryHandler {
boolean exists = false;
double x, y;
JsonObject obj;
long start = -1;
String extra = "";
String elementName;
String script = null;
public ElementCenterQuery(String queryName, String name) {
super(queryName);
elementName = name;
}
public ElementCenterQuery addAdditional(String key, String value) {
extra += "'," + key + ":' + " + value + " +";
script = null;
return this;
}
@Override
public boolean handle(CefBrowser browser, CefFrame frame, JsonObject data, boolean persistent, CefQueryCallback callback) {
exists = data.getAsJsonPrimitive("exists").getAsBoolean();
if (exists) {
x = data.getAsJsonPrimitive("x").getAsDouble() + data.getAsJsonPrimitive("w").getAsDouble() / 2;
y = data.getAsJsonPrimitive("y").getAsDouble() + data.getAsJsonPrimitive("h").getAsDouble() / 2;
}
obj = data;
start = -1;
callback.success("Success");
return true;
}
public void dispatch(CefBrowser browser) {
if (script == null) {
script = Scripts.QUERY_ELEMENT
.replace("%type%", elementName)
.replace("%Type%", name)
.replace("%extra%", extra)
;
}
if (start == -1) {
browser.executeJavaScript(
script,
"CenterQuery",
0
);
start = System.currentTimeMillis();
} else {
long ms = System.currentTimeMillis();
if (start + 1000 < ms) {
browser.executeJavaScript(
script,
"CenterQuery",
0
);
start = System.currentTimeMillis();
}
}
}
public boolean hasFocused() {
return exists;
}
public double getX() {
return x;
}
public double getY() {
return y;
}
public JsonObject getObj() {
return obj;
}
}

View File

@ -0,0 +1,32 @@
package net.montoyo.wd.utilities.browser.handlers.js.queries;
import com.google.gson.JsonObject;
import net.montoyo.wd.utilities.browser.WDBrowser;
import net.montoyo.wd.utilities.browser.handlers.js.JSQueryHandler;
import net.montoyo.wd.utilities.math.Vector2i;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.callback.CefQueryCallback;
public class GetSizeQuery extends JSQueryHandler {
public GetSizeQuery() {
super("GetSize");
}
@Override
public boolean handle(CefBrowser browser, CefFrame frame, JsonObject data, boolean persistent, CefQueryCallback callback) {
if (browser instanceof WDBrowser wdBrowser) {
if (wdBrowser.getSide() != null) {
Vector2i sz = wdBrowser.getBe().getScreen(
wdBrowser.getSide()
).size;
callback.success(
"{\"x\":" + sz.x + ",\"y\":" + sz.y + "}"
);
return true;
}
}
callback.failure(404, "Screen has been removed.");
return true;
}
}

View File

@ -7,4 +7,8 @@ public-f net.minecraft.world.phys.AABB f_82289_
public-f net.minecraft.world.phys.AABB f_82290_
public-f net.minecraft.world.phys.AABB f_82291_
public-f net.minecraft.world.phys.AABB f_82292_
public-f net.minecraft.world.phys.AABB f_82293_
public-f net.minecraft.world.phys.AABB f_82293_
# GameRenderer
public net.minecraft.client.renderer.GameRenderer m_109141_(Lnet/minecraft/client/Camera;FZ)D # getFov

View File

@ -2,7 +2,7 @@ modLoader="javafml" #mandatory
loaderVersion="[43,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions.
license="Public Domain"
license="MIT"
issueTrackerURL="" #optional
@ -10,17 +10,17 @@ issueTrackerURL="" #optional
modId="webdisplays" #mandatory
version="1.3.3" #mandatory
version="2.0.1-1.20.1" #mandatory
displayName="WebDisplays" #mandatory
displayURL="https://github.com/Mysticpasta1/webdisplays" #optional
displayURL="https://github.com/CinemaMod/webdisplays" #optional
logoFile= "" #optional
credits="" #optional
authors="Mysticpasta1, WaterPicker, Montoyo, GiantLuigi4" #optional
authors="GiantLuigi4, ds58, Mysticpasta1, montoyo, WaterPicker" #optional
description='''
'''
@ -42,6 +42,6 @@ side="BOTH"
[[dependencies.webdisplays]]
modId="mcef"
mandatory=true
versionRange="[1.20.1-2.4.17, )"
versionRange="[2.0.0+1.20.1, )"
ordering="NONE"
side="BOTH"

View File

@ -59,6 +59,18 @@
#wd {
padding-left: 20px;
}
.info {
position: fixed;
bottom: 0px;
right: 0px;
padding: 10px;
}
.no_margin {
margin: 0px;
color: #5555ff;
}
</style>
</head>
<body>
@ -78,5 +90,8 @@
</td>
</tr>
</table>
<div class="info"><p class="no_margin">
Crouch+Click on the screen to set the URL
</p></div>
</body>
</html>

View File

@ -5,7 +5,7 @@
*/
function wdExecRequest(name, func) {
window.mcefQuery({ request: "WebDisplays_" + name,
window.cefQuery({ request: "WebDisplays_" + name,
persistent: true,
onSuccess: function(response) {
try {

View File

@ -0,0 +1,12 @@
{
let mev = new MouseEvent("mousemove", {
view: window,
bubbles: true,
cancelable: false,
clientX: %xCoord%,
clientY: %yCoord%,
movementX: %xDelta%,
movementY: %yDelta%
});
document.pointerLockElement.dispatchEvent(mev);
}

View File

@ -0,0 +1,47 @@
{
const elemRef = { element: undefined, unadjusted: false };
Document.prototype.__defineGetter__("pointerLockElement", () => {
return elemRef['element'];
});
Document.prototype.__defineGetter__("webdisplays__unadjustPointerMotion", () => elemRef['unadjusted']);
Document.prototype.__defineSetter__("pointerLockElement", (v) => {});
Element.prototype.requestPointerLock = function(unadjustedMovement = false) {
elemRef['element'] = this;
elemRef['unadjusted'] = unadjustedMovement;
document.pointerLockElement = elemRef['element'];
let bodyRect = document.body.getBoundingClientRect();
let elemRect = this.getBoundingClientRect();
let doc = document;
window.cefQuery({
request:
'WebDisplays_PointerElement{' +
'exists:true,' +
'x:' + (elemRect.left) + ',' +
'y:' + (elemRect.top) + ',' +
'w:' + ((elemRect.right - elemRect.left)) + ',' +
'h:' + ((elemRect.bottom - elemRect.top)) + ',' +
'unadjust:' + document.webdisplays__unadjustPointerMotion +
'}', onSuccess: function(response) {
doc.dispatchEvent(new Event("pointerlockchange"));
},
onFailure: function(error_code, error_message) {
doc.dispatchEvent(new Event("pointerlockerror"));
}
});
}
Document.prototype.exitPointerLock = () => {
elemRef['element'] = undefined;
elemRef['unadjusted'] = false;
document.pointerLockElement = elemRef['element'];
window.cefQuery({
request: 'WebDisplays_PointerElement{exists: false}',
onSuccess: function(response) {},
onFailure: function(error_code, error_message) {}
});
}
}

View File

@ -0,0 +1,31 @@
try {
let focusedElement = %type%;
if (focusedElement == null || focusedElement == document.body) {
window.cefQuery({
request: 'WebDisplays_%Type%{exists: false}',
onSuccess: function(response) {},
onFailure: function(error_code, error_message) {}
});
} else {
let bodyRect = document.body.getBoundingClientRect();
let elemRect = focusedElement.getBoundingClientRect();
window.cefQuery({
request: 'WebDisplays_%Type%{' +
'exists:true,' +
'x:' + (elemRect.left) + ',' +
'y:' + (elemRect.top) + ',' +
'w:' + ((elemRect.right - elemRect.left)) + ',' +
'h:' + ((elemRect.bottom - elemRect.top)) + %extra%
'}', onSuccess: function(response) {},
onFailure: function(error_code, error_message) {}
});
}
} catch (err) {
console.error(err);
window.cefQuery({
request: 'WebDisplays_%Type%{exists: false}',
onSuccess: function(response) {},
onFailure: function(error_code, error_message) {}
});
}

View File

@ -7,7 +7,6 @@
"block.webdisplays.ccinterface": "ComputerCraft Interface",
"block.webdisplays.cointerface": "OpenComputers Interface",
"block.webdisplays.redctrl": "Redstone Controller",
"block.webdisplays.redctrl": "Redstone Controller",
"block.webdisplays.server": "Server",
"item.webdisplays.screencfg": "Screen Configurator",
"item.webdisplays.ownerthief": "Ownership Thief [ADMIN]",
@ -154,5 +153,7 @@
"webdisplays.server.help.upload": "Opens the upload wizard",
"webdisplays.server.help.rm": "Deletes a file",
"webdisplays.server.help.reconnect": "Reconnect to miniserv [DEBUG]",
"webdisplays.server.press_for_more": "<Press DOWN to show more>"
"webdisplays.server.press_for_more": "<Press DOWN to show more>",
"webdisplays.key.toggle_mouse": "Toggle Laser Mouse (WebDisplays)",
"webdisplays.gui.minepad.close": "Press Shift and Escape to close."
}

View File

@ -0,0 +1,156 @@
{
"itemGroup.webdisplays": "§5Web Displays",
"block.webdisplays.screen": "Ecran",
"block.webdisplays.peripheral": "Périphérique",
"block.webdisplays.kb_left": "Clavier",
"block.webdisplays.rctrl": "Télécommande",
"block.webdisplays.ccinterface": "Interface ComputerCraft",
"block.webdisplays.cointerface": "Interface OpenComputers",
"block.webdisplays.redctrl": "Contrôleur",
"block.webdisplays.server": "Serveur",
"item.webdisplays.screencfg": "Gestionnaire d'écran",
"item.webdisplays.ownerthief": "Voleur d'écran [ADMIN]",
"item.webdisplays.linker": "Outil d'appairage",
"item.webdisplays.craftcomp": "Ingrédient de recette",
"item.webdisplays.craftcomp_stonekey": "Touche en pierre",
"item.webdisplays.craftcomp_upgrade": "Amélioration vide",
"item.webdisplays.craftcomp_peripheral": "Base de périphérique",
"item.webdisplays.craftcomp_batcell": "Cellule de batterie",
"item.webdisplays.craftcomp_batpack": "Batterie",
"item.webdisplays.craftcomp_laserdiode": "Diode laser 650nm",
"item.webdisplays.craftcomp_backlight": "Rétroéclairage",
"item.webdisplays.craftcomp_extcard": "Carte d'extension",
"item.webdisplays.craftcomp_badextcard": "Carte d'extension ratée",
"item.webdisplays.minepad": "minePad",
"item.webdisplays.minepad2": "minePad 2",
"item.webdisplays.upgrade": "Amélioration d'écran",
"item.webdisplays.upgrade_lasermouse": "Capteur de laser",
"item.webdisplays.upgrade_redinput": "Port d'entrée Redstone",
"item.webdisplays.upgrade_redoutput": "Port de sortie Redstone",
"item.webdisplays.upgrade_gps": "Module GALILEO",
"item.webdisplays.laserpointer": "Pointeur laser",
"item.webdisplays.advicon": "Icone de progrès",
"item.webdisplays.advicon.wd": "WebDisplays",
"item.webdisplays.advicon.brokenpad": "minePad cassé",
"item.webdisplays.advicon.pigeon": "Pigeon",
"item.webdisplays.wiki": "Appuyez sur \"F1\" pour ouvrir le Wiki",
"webdisplays.message.tooSmall": "Trop petit ! La taille minimale est de 2x2.",
"webdisplays.message.tooBig": "Trop grand ! La taille maximale est de %dx%d.",
"webdisplays.message.invalid": "La structure est invalide; regardez vers %s.",
"webdisplays.message.turnOn": "Vous devez d'abord allumer l'écran",
"webdisplays.message.screenSet": "Ecran sélectionné ! Donnez l'item à quelqu'un...",
"webdisplays.message.newOwner": "Vous êtes maintenant le propriétaire de cet écran",
"webdisplays.message.restrictions": "Vous n'avez pas le droit de faire cela :(",
"webdisplays.message.peripheral": "Ce n'est pas un périphérique!",
"webdisplays.message.linked": "Appairé !",
"webdisplays.message.linkError": "Erreur d'appairage... :( vérifiez les logs",
"webdisplays.message.notAScreen": "Vous devez d'abord sélectionner un écran",
"webdisplays.message.screenSet2": "Ecran sélectionné! Maintenant, sélectionnez un périphérique...",
"webdisplays.message.chunkUnloaded": "Le chunk dans lequel se trouve l'écran n'est pas chargé",
"webdisplays.message.notLinked": "Ce périphérique n'a pas encore été appairé",
"webdisplays.message.missingCC": "ComputerCraft non disponible.",
"webdisplays.message.missingOC": "OpenComputers non disponible.",
"webdisplays.message.upgradeError": "Erreur lors de l'amélioration, vérifiez les logs",
"webdisplays.message.upgradeOk": "Amélioration installée !",
"webdisplays.message.linkAbort": "Outil d'appairage remis à zéro.",
"webdisplays.message.noMiniserv": "Le bloc serveur est désactivé",
"webdisplays.message.otDisabled": "Le voleur d'écran est désactivé sur ce serveur",
"webdisplays.message.welcome1": "Merci d'avoir installé WebDisplays ! Pour obtenir",
"webdisplays.message.welcome2": "de l'aide sur un item, passez la souris dessus",
"webdisplays.message.welcome3": "et appuyez sur F1. Amusez-vous bien, - montoyo",
"webdisplays.gui.screencfg.owner": "Propriétaire :",
"webdisplays.gui.screencfg.friends": "Amis :",
"webdisplays.gui.screencfg.permissions": "Permissions :",
"webdisplays.gui.screencfg.seturl": "Changer l'URL",
"webdisplays.gui.screencfg.click": "Cliquer & écrire",
"webdisplays.gui.screencfg.friendlist": "Gérer les amis",
"webdisplays.gui.screencfg.otherrights": "Gérer les autres",
"webdisplays.gui.screencfg.mupgrades": "Améliorer & appairer",
"webdisplays.gui.screencfg.mres": "Changer la résolution",
"webdisplays.gui.screencfg.others": "Autres",
"webdisplays.gui.screencfg.upgrades": "Améliorations :",
"webdisplays.gui.screencfg.resolution": "Résolution :",
"webdisplays.gui.screencfg.setres": "Déf. Resolution",
"webdisplays.gui.screencfg.rotation": "Rotation",
"webdisplays.gui.screencfg.rot0": "0°",
"webdisplays.gui.screencfg.rot90": "90°",
"webdisplays.gui.screencfg.rot180": "180°",
"webdisplays.gui.screencfg.rot270": "270°",
"webdisplays.gui.screencfg.lockratio": "Verrouiller le ratio",
"webdisplays.gui.screencfg.autovol": "Volume Auto",
"webdisplays.gui.screencfg.avwarning": "§cAttention !\nLe volume auto ne fonctionne\nque sur les vidéos YouTube, et\nsi il a été activé dans la config !",
"webdisplays.linker.selectScreen": "Cliquez droit sur un écran",
"webdisplays.linker.selectPeripheral": "Cliquez droit sur un périphérique",
"webdisplays.linker.posInfo": "Pos. écran : %d %d %d",
"webdisplays.linker.sideInfo": "Côté : %s",
"webdisplays.gui.seturl.url": "URL:",
"webdisplays.gui.seturl.ok": "OK",
"webdisplays.gui.seturl.cancel": "Annuler",
"webdisplays.gui.seturl.shutdown": "Eteindre",
"webdisplays.minepad.turnon": "Pour allumer, s'accroupir et faire clique-droit",
"webdisplays.minepad2.info": "PAS DE REMBOURSEMENT !",
"webdisplays.extcard.cantcraft1": "Vous n'avez pas assez de connaissances",
"webdisplays.extcard.cantcraft2": "Vous ALLEZ RATER la fabrication de cet item",
"webdisplays.extcard.bad": "Quelqu'un a raté la fabrication de cette carte",
"webdisplays.gui.keyboard.hooked": "Clavier attaché. Appuyez sur Echap pour quitter.",
"webdisplays.gui.keyboard.warning1": "ATTENTION ! Le texte que vous tapez ici est envoyé EN CLAIR au serveur.",
"webdisplays.gui.keyboard.warning2": "Cela signifie que tout le monde peut voir ce que vous tapez.",
"webdisplays.gui.keyboard.warning3": "Ne JAMAIS taper de mot de passe avec ce clavier.",
"webdisplays.gui.keyboard.gotcha": "Je comprends",
"advancements.webdisplays.root.title": "WebDisplays",
"advancements.webdisplays.root.description": "Le mod WebDisplays",
"advancements.webdisplays.screen.title": "The internet is for...",
"advancements.webdisplays.screen.description": "Fabriquez votre premier écran",
"advancements.webdisplays.minepad.title": "C'est une révolution",
"advancements.webdisplays.minepad.description": "Essayez notre tout dernier bijoux technologique: le minePad !",
"advancements.webdisplays.padbreak.title": "Rétro-ingénierie",
"advancements.webdisplays.padbreak.description": "Ces trucs là sont fragiles ! Ne les jetez pas de haut pour débloquer les recettes des améliorations !",
"advancements.webdisplays.minepad2.title": "Pigeon",
"advancements.webdisplays.minepad2.description": "Fabriquez un minePad 2. Ecoutez, je sais que c'est très cher, mais ça veut dire que ce qu'il y'a de mieux non ?",
"advancements.webdisplays.linkperipheral.title": "Sans fils !",
"advancements.webdisplays.linkperipheral.description": "Appairez un périphérique à un écran",
"advancements.webdisplays.keyboardcat.title": "PU**IN DE CHATS",
"advancements.webdisplays.keyboardcat.description": "Faites en sorte qu'un ocelot marche sur votre clavier",
"advancements.webdisplays.upgrade.title": "Plus qu'un écran",
"advancements.webdisplays.upgrade.description": "Installez votre première amélioration",
"advancements.webdisplays.laser.title": "Ne pas viser les yeux",
"advancements.webdisplays.laser.description": "Fabriquez un pointeur laser",
"webdisplays.side.bottom": "Bas",
"webdisplays.side.top": "Haut",
"webdisplays.side.north": "Nord",
"webdisplays.side.south": "Sud",
"webdisplays.side.west": "Ouest",
"webdisplays.side.east": "Est",
"webdisplays.server.info": "Tapez \"help\" pour obtenir de l'aide",
"webdisplays.server.unknowncmd": "Commande inconnue.",
"webdisplays.server.error": "Erreur interne. Vérifiez les logs.",
"webdisplays.server.error2": "Erreur interne %d. Vérifiez les logs.",
"webdisplays.server.argerror": "Argument non reconnu",
"webdisplays.server.queryerr": "Erreur de requête, essayez \"reconnect\".",
"webdisplays.server.errowner": "Seul le propriétaire peut faire cela.",
"webdisplays.server.timeout": "Le délai est dépaissé. Vérifiez les logs.",
"webdisplays.server.ownername": "Nom du propriétaire : %s",
"webdisplays.server.owneruuid": "UUID du propriétaire :",
"webdisplays.server.quota": "%s/%s utilisés",
"webdisplays.server.fnamearg": "Nom de fichier manquant",
"webdisplays.server.nameerr": "Nom de fichier invalide",
"webdisplays.server.urlcopied": "URL copié dans le presse-papier.",
"webdisplays.server.notfound": "Fichier introuvable",
"webdisplays.server.upload.info": "Choisissez un fichier",
"webdisplays.server.upload.parent": "[Dossier parent]",
"webdisplays.server.upload.uploading": "Mise en ligne...",
"webdisplays.server.upload.done": "Terminé !",
"webdisplays.server.upload.exists": "Erreur : le fichier existe",
"webdisplays.server.upload.quota": "Erreur : taille max atteinte",
"webdisplays.server.help.help": "Affiche ce texte",
"webdisplays.server.help.clear": "Efface l'écran",
"webdisplays.server.help.exit": "Quitte la console",
"webdisplays.server.help.access": "§kPas d'aide disponible",
"webdisplays.server.help.owner": "Affiche le proprio. de ce serveur",
"webdisplays.server.help.quota": "Affiche le quota de stockage",
"webdisplays.server.help.ls": "Liste les fichiers de ce serveur",
"webdisplays.server.help.url": "Copie l'URL d'un fichier",
"webdisplays.server.help.upload": "Ouvre l'assitant de mise en ligne",
"webdisplays.server.help.rm": "Supprime un fichier",
"webdisplays.server.help.reconnect": "Se reco. à Miniserv [DEBUG]"
}

View File

@ -1,154 +0,0 @@
itemGroup.webdisplays=§5Web Displays
tile.webdisplays.screen.name=Ecran
tile.webdisplays.peripheral.name=Périphérique
tile.webdisplays.peripheral.keyboard.name=Clavier
tile.webdisplays.peripheral.remotectrl.name=Télécommande
tile.webdisplays.peripheral.ccinterface.name=Interface ComputerCraft
tile.webdisplays.peripheral.cointerface.name=Interface OpenComputers
tile.webdisplays.peripheral.redstonectrl.name=Contrôleur
tile.webdisplays.peripheral.server.name=Serveur
item.webdisplays.screencfg.name=Gestionnaire d'écran
item.webdisplays.ownerthief.name=Voleur d'écran [ADMIN]
item.webdisplays.linker.name=Outil d'appairage
item.webdisplays.craftcomp.name=Ingrédient de recette
item.webdisplays.craftcomp.stonekey.name=Touche en pierre
item.webdisplays.craftcomp.upgrade.name=Amélioration vide
item.webdisplays.craftcomp.peripheral.name=Base de périphérique
item.webdisplays.craftcomp.batcell.name=Cellule de batterie
item.webdisplays.craftcomp.batpack.name=Batterie
item.webdisplays.craftcomp.laserdiode.name=Diode laser 650nm
item.webdisplays.craftcomp.backlight.name=Rétroéclairage
item.webdisplays.craftcomp.extcard.name=Carte d'extension
item.webdisplays.craftcomp.badextcard.name=Carte d'extension ratée
item.webdisplays.minepad.name=minePad
item.webdisplays.minepad2.name=minePad 2
item.webdisplays.upgrade.name=Amélioration d'écran
item.webdisplays.upgrade.lasermouse.name=Capteur de laser
item.webdisplays.upgrade.redinput.name=Port d'entrée Redstone
item.webdisplays.upgrade.redoutput.name=Port de sortie Redstone
item.webdisplays.upgrade.gps.name=Module GALILEO
item.webdisplays.laserpointer.name=Pointeur laser
item.webdisplays.advicon.name=Icone de progrès
item.webdisplays.advicon.wd.name=WebDisplays
item.webdisplays.advicon.brokenpad.name=minePad cassé
item.webdisplays.advicon.pigeon.name=Pigeon
item.webdisplays.wiki=Appuyez sur "F1" pour ouvrir le Wiki
webdisplays.message.tooSmall=Trop petit ! La taille minimale est de 2x2.
webdisplays.message.tooBig=Trop grand ! La taille maximale est de %dx%d.
webdisplays.message.invalid=La structure est invalide; regardez vers %s.
webdisplays.message.turnOn=Vous devez d'abord allumer l'écran
webdisplays.message.screenSet=Ecran sélectionné ! Donnez l'item à quelqu'un...
webdisplays.message.newOwner=Vous êtes maintenant le propriétaire de cet écran
webdisplays.message.restrictions=Vous n'avez pas le droit de faire cela :(
webdisplays.message.peripheral=Ce n'est pas un périphérique!
webdisplays.message.linked=Appairé !
webdisplays.message.linkError=Erreur d'appairage... :( vérifiez les logs
webdisplays.message.notAScreen=Vous devez d'abord sélectionner un écran
webdisplays.message.screenSet2=Ecran sélectionné! Maintenant, sélectionnez un périphérique...
webdisplays.message.chunkUnloaded=Le chunk dans lequel se trouve l'écran n'est pas chargé
webdisplays.message.notLinked=Ce périphérique n'a pas encore été appairé
webdisplays.message.missingCC=ComputerCraft non disponible.
webdisplays.message.missingOC=OpenComputers non disponible.
webdisplays.message.upgradeError=Erreur lors de l'amélioration, vérifiez les logs
webdisplays.message.upgradeOk=Amélioration installée !
webdisplays.message.linkAbort=Outil d'appairage remis à zéro.
webdisplays.message.noMiniserv=Le bloc serveur est désactivé
webdisplays.message.otDisabled=Le voleur d'écran est désactivé sur ce serveur
webdisplays.message.welcome1=Merci d'avoir installé WebDisplays ! Pour obtenir
webdisplays.message.welcome2=de l'aide sur un item, passez la souris dessus
webdisplays.message.welcome3=et appuyez sur F1. Amusez-vous bien, - montoyo
webdisplays.gui.screencfg.owner=Propriétaire :
webdisplays.gui.screencfg.friends=Amis :
webdisplays.gui.screencfg.permissions=Permissions :
webdisplays.gui.screencfg.seturl=Changer l'URL
webdisplays.gui.screencfg.click=Cliquer & écrire
webdisplays.gui.screencfg.friendlist=Gérer les amis
webdisplays.gui.screencfg.otherrights=Gérer les autres
webdisplays.gui.screencfg.mupgrades=Améliorer & appairer
webdisplays.gui.screencfg.mres=Changer la résolution
webdisplays.gui.screencfg.others=Autres
webdisplays.gui.screencfg.upgrades=Améliorations :
webdisplays.gui.screencfg.resolution=Résolution :
webdisplays.gui.screencfg.setres=Déf. Resolution
webdisplays.gui.screencfg.rotation=Rotation
webdisplays.gui.screencfg.rot0=0°
webdisplays.gui.screencfg.rot90=90°
webdisplays.gui.screencfg.rot180=180°
webdisplays.gui.screencfg.rot270=270°
webdisplays.gui.screencfg.lockratio=Verrouiller le ratio
webdisplays.gui.screencfg.autovol=Volume Auto
webdisplays.gui.screencfg.avwarning=§cAttention !\nLe volume auto ne fonctionne\nque sur les vidéos YouTube, et\nsi il a été activé dans la config !
webdisplays.linker.selectScreen=Cliquez droit sur un écran
webdisplays.linker.selectPeripheral=Cliquez droit sur un périphérique
webdisplays.linker.posInfo=Pos. écran : %d %d %d
webdisplays.linker.sideInfo=Côté : %s
webdisplays.gui.seturl.url=URL:
webdisplays.gui.seturl.ok=OK
webdisplays.gui.seturl.cancel=Annuler
webdisplays.gui.seturl.shutdown=Eteindre
webdisplays.minepad.turnon=Pour allumer, s'accroupir et faire clique-droit
webdisplays.minepad2.info=PAS DE REMBOURSEMENT !
webdisplays.extcard.cantcraft1=Vous n'avez pas assez de connaissances
webdisplays.extcard.cantcraft2=Vous ALLEZ RATER la fabrication de cet item
webdisplays.extcard.bad=Quelqu'un a raté la fabrication de cette carte
webdisplays.gui.keyboard.hooked=Clavier attaché. Appuyez sur Echap pour quitter.
webdisplays.gui.keyboard.warning1=ATTENTION ! Le texte que vous tapez ici est envoyé EN CLAIR au serveur.
webdisplays.gui.keyboard.warning2=Cela signifie que tout le monde peut voir ce que vous tapez.
webdisplays.gui.keyboard.warning3=Ne JAMAIS taper de mot de passe avec ce clavier.
webdisplays.gui.keyboard.gotcha=Je comprends
advancements.webdisplays.root.title=WebDisplays
advancements.webdisplays.root.description=Le mod WebDisplays
advancements.webdisplays.screen.title=The internet is for...
advancements.webdisplays.screen.description=Fabriquez votre premier écran
advancements.webdisplays.minepad.title=C'est une révolution
advancements.webdisplays.minepad.description=Essayez notre tout dernier bijoux technologique: le minePad !
advancements.webdisplays.padbreak.title=Rétro-ingénierie
advancements.webdisplays.padbreak.description=Ces trucs là sont fragiles ! Ne les jetez pas de haut pour débloquer les recettes des améliorations !
advancements.webdisplays.minepad2.title=Pigeon
advancements.webdisplays.minepad2.description=Fabriquez un minePad 2. Ecoutez, je sais que c'est très cher, mais ça veut dire que ce qu'il y'a de mieux non ?
advancements.webdisplays.linkperipheral.title=Sans fils !
advancements.webdisplays.linkperipheral.description=Appairez un périphérique à un écran
advancements.webdisplays.keyboardcat.title=PU**IN DE CHATS
advancements.webdisplays.keyboardcat.description=Faites en sorte qu'un ocelot marche sur votre clavier
advancements.webdisplays.upgrade.title=Plus qu'un écran
advancements.webdisplays.upgrade.description=Installez votre première amélioration
advancements.webdisplays.laser.title=Ne pas viser les yeux
advancements.webdisplays.laser.description=Fabriquez un pointeur laser
webdisplays.side.bottom=Bas
webdisplays.side.top=Haut
webdisplays.side.north=Nord
webdisplays.side.south=Sud
webdisplays.side.west=Ouest
webdisplays.side.east=Est
webdisplays.server.info=Tapez "help" pour obtenir de l'aide
webdisplays.server.unknowncmd=Commande inconnue.
webdisplays.server.error=Erreur interne. Vérifiez les logs.
webdisplays.server.error2=Erreur interne %d. Vérifiez les logs.
webdisplays.server.argerror=Argument non reconnu
webdisplays.server.queryerr=Erreur de requête, essayez "reconnect".
webdisplays.server.errowner=Seul le propriétaire peut faire cela.
webdisplays.server.timeout=Le délai est dépaissé. Vérifiez les logs.
webdisplays.server.ownername=Nom du propriétaire : %s
webdisplays.server.owneruuid=UUID du propriétaire :
webdisplays.server.quota=%s/%s utilisés
webdisplays.server.fnamearg=Nom de fichier manquant
webdisplays.server.nameerr=Nom de fichier invalide
webdisplays.server.urlcopied=URL copié dans le presse-papier.
webdisplays.server.notfound=Fichier introuvable
webdisplays.server.upload.info=Choisissez un fichier
webdisplays.server.upload.parent=[Dossier parent]
webdisplays.server.upload.uploading=Mise en ligne...
webdisplays.server.upload.done=Terminé !
webdisplays.server.upload.exists=Erreur : le fichier existe
webdisplays.server.upload.quota=Erreur : taille max atteinte
webdisplays.server.help.help=Affiche ce texte
webdisplays.server.help.clear=Efface l'écran
webdisplays.server.help.exit=Quitte la console
webdisplays.server.help.access=§kPas d'aide disponible
webdisplays.server.help.owner=Affiche le proprio. de ce serveur
webdisplays.server.help.quota=Affiche le quota de stockage
webdisplays.server.help.ls=Liste les fichiers de ce serveur
webdisplays.server.help.url=Copie l'URL d'un fichier
webdisplays.server.help.upload=Ouvre l'assitant de mise en ligne
webdisplays.server.help.rm=Supprime un fichier
webdisplays.server.help.reconnect=Se reco. à Miniserv [DEBUG]

View File

@ -0,0 +1,151 @@
{
"itemGroup.webdisplays": "§5内置网页浏览器",
"block.webdisplays.screen": "网页屏幕方块",
"block.webdisplays.peripheral": "外部设备",
"block.webdisplays.kb_left": "键盘",
"block.webdisplays.rctrl": "网页输入器",
"block.webdisplays.ccinterface": "ComputerCraft接口",
"block.webdisplays.cointerface": "OpenComputers接口",
"block.webdisplays.redctrl": "红石控制器",
"block.webdisplays.server": "服务器方块",
"item.webdisplays.screencfg": "屏幕配置器",
"item.webdisplays.ownerthief": "屏幕所有权给予器[管理员物品]",
"item.webdisplays.linker": "连接工具",
"item.webdisplays.craftcomp": "制作材料",
"item.webdisplays.craftcomp_stonekey": "石头按钮",
"item.webdisplays.craftcomp_upgrade": "空白升级组件",
"item.webdisplays.craftcomp_peripheral": "外部设备材料",
"item.webdisplays.craftcomp_batcell": "电池",
"item.webdisplays.craftcomp_batpack": "电池组",
"item.webdisplays.craftcomp_laserdiode": "650纳米激光二极管",
"item.webdisplays.craftcomp_backlight": "屏幕背景",
"item.webdisplays.craftcomp_extcard": "扩充卡片",
"item.webdisplays.craftcomp_badextcard": "损坏的扩充卡片",
"item.webdisplays.minepad": "掌上电脑",
"item.webdisplays.minepad2": "第二代掌上电脑",
"item.webdisplays.upgrade": "屏幕升级组件",
"item.webdisplays.upgrade_lasermouse": "激光笔模块",
"item.webdisplays.upgrade_redinput": "红石输入模块",
"item.webdisplays.upgrade_redoutput": "红石输出模块",
"item.webdisplays.upgrade_gps": "GPS模块",
"item.webdisplays.laserpointer": "激光笔",
"item.webdisplays.advicon": "进度图标",
"item.webdisplays.advicon.wd": "内置网页浏览器",
"item.webdisplays.advicon.brokenpad": "损坏的掌上电脑",
"item.webdisplays.advicon.pigeon": "鸽子",
"item.webdisplays.wiki": "点击\"F1\"来打开维基百科",
"webdisplays.message.tooSmall": "这个屏幕太小了!最小尺寸是2x2.",
"webdisplays.message.tooBig": "这个屏幕太大了!最大尺寸是%dx%d.",
"webdisplays.message.invalid": "这个结构无效;你在看着坐标%s.",
"webdisplays.message.turnOn": "你需要先打开这个屏幕!",
"webdisplays.message.screenSet": "屏幕已设置!现在应将这个物品给新的拥有者...",
"webdisplays.message.newOwner": "你现在是这个屏幕的拥有者!",
"webdisplays.message.restrictions": "你不能做这件事 :(",
"webdisplays.message.peripheral": "这不是一个外部设备!",
"webdisplays.message.linked": "已连接!",
"webdisplays.message.linkError": "连接失败 :( 请检查日志...",
"webdisplays.message.notAScreen": "请先右击屏幕...",
"webdisplays.message.screenSet2": "屏幕已设置!现在右击一个外部设备...",
"webdisplays.message.chunkUnloaded": "放置在这个区块的屏幕未开启!",
"webdisplays.message.notLinked": "这个外部设备还未连接.",
"webdisplays.message.missingCC": "ComputerCraft不可用.",
"webdisplays.message.missingOC": "OpenComputers不可用.",
"webdisplays.message.upgradeError": "升级组件安装失败 :( 请检查日志...",
"webdisplays.message.upgradeOk": "升级组件已安装!",
"webdisplays.message.linkAbort": "连接器复位",
"webdisplays.message.noMiniserv": "服务器方块在这个服务器不可用",
"webdisplays.message.otDisabled": "屏幕所有权给予者在这个服务器不可用",
"webdisplays.gui.screencfg.owner": "屏幕拥有者:",
"webdisplays.gui.screencfg.friends": "朋友:",
"webdisplays.gui.screencfg.permissions": "权限:",
"webdisplays.gui.screencfg.seturl": "修改网址",
"webdisplays.gui.screencfg.click": "点击和输入",
"webdisplays.gui.screencfg.friendlist": "管理朋友",
"webdisplays.gui.screencfg.otherrights": "管理其他人",
"webdisplays.gui.screencfg.mupgrades": "安装升级组件和连接",
"webdisplays.gui.screencfg.mres": "改变分辨率",
"webdisplays.gui.screencfg.others": "其他人",
"webdisplays.gui.screencfg.upgrades": "升级组件:",
"webdisplays.gui.screencfg.resolution": "当前分辨率:",
"webdisplays.gui.screencfg.setres": "设置分辨率",
"webdisplays.gui.screencfg.rotation": "角度",
"webdisplays.gui.screencfg.rot0": "0°",
"webdisplays.gui.screencfg.rot90": "90°",
"webdisplays.gui.screencfg.rot180": "180°",
"webdisplays.gui.screencfg.rot270": "270°",
"webdisplays.gui.screencfg.lockratio": "锁定分辨率比例",
"webdisplays.linker.selectScreen": "右击一个屏幕",
"webdisplays.linker.selectPeripheral": "右击一个外部设备",
"webdisplays.linker.posInfo": "屏幕位置:%d %d %d",
"webdisplays.linker.sideInfo": "地址:%s",
"webdisplays.gui.seturl.url": "网址:",
"webdisplays.gui.seturl.ok": "确定",
"webdisplays.gui.seturl.cancel": "取消",
"webdisplays.gui.seturl.shutdown": "停止",
"webdisplays.minepad.turnon": "潜行并右击来打开",
"webdisplays.minepad2.info": "不退款!",
"webdisplays.extcard.cantcraft1": "你知道的还不够多.",
"webdisplays.extcard.cantcraft2": "你不能制作这个物品.",
"webdisplays.extcard.bad": "有人制作扩展卡片失败了",
"webdisplays.gui.keyboard.hooked": "已连接至你的键盘.请按ESC离开界面.",
"webdisplays.gui.keyboard.warning1": "警告!你输入的数据都将以纯文本形式发送到服务器.",
"webdisplays.gui.keyboard.warning2": "这意味着任何人都可以知道你在输入什么.",
"webdisplays.gui.keyboard.warning3": "永远都不要用这个键盘输入你的密码.",
"webdisplays.gui.keyboard.gotcha": "被我耍到啦",
"advancements.webdisplays.root.title": "内置网页浏览器",
"advancements.webdisplays.root.description": "内置网页浏览器Mod",
"advancements.webdisplays.screen.title": "互联网是...",
"advancements.webdisplays.screen.description": "制作你第一个屏幕方块",
"advancements.webdisplays.minepad.title": "这可是个革命",
"advancements.webdisplays.minepad.description": "你得到了最新的科技:掌上电脑",
"advancements.webdisplays.padbreak.title": "逆向工程",
"advancements.webdisplays.padbreak.description": "这些东西易碎!不要为了得到升级组件材料而让掌上电脑从高空坠落",
"advancements.webdisplays.minepad2.title": "鸽子",
"advancements.webdisplays.minepad2.description": "制作第二代掌上电脑.看,我知道它有多贵,但这意味着它比任何东西都好,对吧?...对吧?",
"advancements.webdisplays.linkperipheral.title": "这是无线的!",
"advancements.webdisplays.linkperipheral.description": "连接外部设备到屏幕",
"advancements.webdisplays.keyboardcat.title": "可恶的猫",
"advancements.webdisplays.keyboardcat.description": "让一只猫踩过你的键盘",
"advancements.webdisplays.upgrade.title": "不仅只是一个屏幕",
"advancements.webdisplays.upgrade.description": "安装了你的第一个升级组件",
"advancements.webdisplays.laser.title": "不要对着眼睛",
"advancements.webdisplays.laser.description": "制作激光笔!",
"webdisplays.side.bottom": "底部",
"webdisplays.side.top": "顶部",
"webdisplays.side.north": "北方",
"webdisplays.side.south": "南方",
"webdisplays.side.west": "西方",
"webdisplays.side.east": "东方",
"webdisplays.server.info": "如果你需要帮助请输入\"help\".",
"webdisplays.server.unknowncmd": "未知指令.",
"webdisplays.server.error": "内部错误.请检查日志.",
"webdisplays.server.error2": "内部错误%d.请检查日志.",
"webdisplays.server.argerror": "未识别的参数.",
"webdisplays.server.queryerr": "查询错误,尝试输入\"reconnect\".",
"webdisplays.server.errowner": "只有拥有者才可以访问.",
"webdisplays.server.timeout": "查询超时.请检查日志.",
"webdisplays.server.ownername": "拥有者名称:%s",
"webdisplays.server.owneruuid": "拥有者UUID:",
"webdisplays.server.quota": "%s/%s已使用",
"webdisplays.server.fnamearg": "丢失文件名参数",
"webdisplays.server.nameerr": "无效的文件名",
"webdisplays.server.urlcopied": "复制网址到你的剪切板.",
"webdisplays.server.notfound": "文件未找到",
"webdisplays.server.upload.info": "选择一个文件来上传",
"webdisplays.server.upload.parent": "[父目录]",
"webdisplays.server.upload.uploading": "上传中...",
"webdisplays.server.upload.done": "完成",
"webdisplays.server.upload.exists": "错误:文件不存在",
"webdisplays.server.upload.quota": "错误:文件过大",
"webdisplays.server.help.help": "显示这段文本",
"webdisplays.server.help.clear": "清空这个屏幕",
"webdisplays.server.help.exit": "离开这个控制台",
"webdisplays.server.help.access": "§k无帮助文件",
"webdisplays.server.help.owner": "显示服务器的拥有者",
"webdisplays.server.help.quota": "显示存储空间",
"webdisplays.server.help.ls": "显示这个服务器的文件",
"webdisplays.server.help.url": "复制一个文件网址到你的剪切板",
"webdisplays.server.help.upload": "打开上传向导",
"webdisplays.server.help.rm": "删除一个文件",
"webdisplays.server.help.reconnect": "重新连接到服务器[调试中]"
}

View File

@ -1,149 +0,0 @@
itemGroup.webdisplays=§5内置网页浏览器
tile.webdisplays.screen.name=网页屏幕方块
tile.webdisplays.peripheral.name=外部设备
tile.webdisplays.peripheral.keyboard.name=键盘
tile.webdisplays.peripheral.remotectrl.name=网页输入器
tile.webdisplays.peripheral.ccinterface.name=ComputerCraft接口
tile.webdisplays.peripheral.cointerface.name=OpenComputers接口
tile.webdisplays.peripheral.redstonectrl.name=红石控制器
tile.webdisplays.peripheral.server.name=服务器方块
item.webdisplays.screencfg.name=屏幕配置器
item.webdisplays.ownerthief.name=屏幕所有权给予器[管理员物品]
item.webdisplays.linker.name=连接工具
item.webdisplays.craftcomp.name=制作材料
item.webdisplays.craftcomp.stonekey.name=石头按钮
item.webdisplays.craftcomp.upgrade.name=空白升级组件
item.webdisplays.craftcomp.peripheral.name=外部设备材料
item.webdisplays.craftcomp.batcell.name=电池
item.webdisplays.craftcomp.batpack.name=电池组
item.webdisplays.craftcomp.laserdiode.name=650纳米激光二极管
item.webdisplays.craftcomp.backlight.name=屏幕背景
item.webdisplays.craftcomp.extcard.name=扩充卡片
item.webdisplays.craftcomp.badextcard.name=损坏的扩充卡片
item.webdisplays.minepad.name=掌上电脑
item.webdisplays.minepad2.name=第二代掌上电脑
item.webdisplays.upgrade.name=屏幕升级组件
item.webdisplays.upgrade.lasermouse.name=激光笔模块
item.webdisplays.upgrade.redinput.name=红石输入模块
item.webdisplays.upgrade.redoutput.name=红石输出模块
item.webdisplays.upgrade.gps.name=GPS模块
item.webdisplays.laserpointer.name=激光笔
item.webdisplays.advicon.name=进度图标
item.webdisplays.advicon.wd.name=内置网页浏览器
item.webdisplays.advicon.brokenpad.name=损坏的掌上电脑
item.webdisplays.advicon.pigeon.name=鸽子
item.webdisplays.wiki=点击"F1"来打开维基百科
webdisplays.message.tooSmall=这个屏幕太小了!最小尺寸是2x2.
webdisplays.message.tooBig=这个屏幕太大了!最大尺寸是%dx%d.
webdisplays.message.invalid=这个结构无效;你在看着坐标%s.
webdisplays.message.turnOn=你需要先打开这个屏幕!
webdisplays.message.screenSet=屏幕已设置!现在应将这个物品给新的拥有者...
webdisplays.message.newOwner=你现在是这个屏幕的拥有者!
webdisplays.message.restrictions=你不能做这件事 :(
webdisplays.message.peripheral=这不是一个外部设备!
webdisplays.message.linked=已连接!
webdisplays.message.linkError=连接失败 :( 请检查日志...
webdisplays.message.notAScreen=请先右击屏幕...
webdisplays.message.screenSet2=屏幕已设置!现在右击一个外部设备...
webdisplays.message.chunkUnloaded=放置在这个区块的屏幕未开启!
webdisplays.message.notLinked=这个外部设备还未连接.
webdisplays.message.missingCC=ComputerCraft不可用.
webdisplays.message.missingOC=OpenComputers不可用.
webdisplays.message.upgradeError=升级组件安装失败 :( 请检查日志...
webdisplays.message.upgradeOk=升级组件已安装!
webdisplays.message.linkAbort=连接器复位
webdisplays.message.noMiniserv=服务器方块在这个服务器不可用
webdisplays.message.otDisabled=屏幕所有权给予者在这个服务器不可用
webdisplays.gui.screencfg.owner=屏幕拥有者:
webdisplays.gui.screencfg.friends=朋友:
webdisplays.gui.screencfg.permissions=权限:
webdisplays.gui.screencfg.seturl=修改网址
webdisplays.gui.screencfg.click=点击和输入
webdisplays.gui.screencfg.friendlist=管理朋友
webdisplays.gui.screencfg.otherrights=管理其他人
webdisplays.gui.screencfg.mupgrades=安装升级组件和连接
webdisplays.gui.screencfg.mres=改变分辨率
webdisplays.gui.screencfg.others=其他人
webdisplays.gui.screencfg.upgrades=升级组件:
webdisplays.gui.screencfg.resolution=当前分辨率:
webdisplays.gui.screencfg.setres=设置分辨率
webdisplays.gui.screencfg.rotation=角度
webdisplays.gui.screencfg.rot0=0°
webdisplays.gui.screencfg.rot90=90°
webdisplays.gui.screencfg.rot180=180°
webdisplays.gui.screencfg.rot270=270°
webdisplays.gui.screencfg.lockratio=锁定分辨率比例
webdisplays.linker.selectScreen=右击一个屏幕
webdisplays.linker.selectPeripheral=右击一个外部设备
webdisplays.linker.posInfo=屏幕位置:%d %d %d
webdisplays.linker.sideInfo=地址:%s
webdisplays.gui.seturl.url=网址:
webdisplays.gui.seturl.ok=确定
webdisplays.gui.seturl.cancel=取消
webdisplays.gui.seturl.shutdown=停止
webdisplays.minepad.turnon=潜行并右击来打开
webdisplays.minepad2.info=不退款!
webdisplays.extcard.cantcraft1=你知道的还不够多.
webdisplays.extcard.cantcraft2=你不能制作这个物品.
webdisplays.extcard.bad=有人制作扩展卡片失败了
webdisplays.gui.keyboard.hooked=已连接至你的键盘.请按ESC离开界面.
webdisplays.gui.keyboard.warning1=警告!你输入的数据都将以纯文本形式发送到服务器.
webdisplays.gui.keyboard.warning2=这意味着任何人都可以知道你在输入什么.
webdisplays.gui.keyboard.warning3=永远都不要用这个键盘输入你的密码.
webdisplays.gui.keyboard.gotcha=被我耍到啦
advancements.webdisplays.root.title=内置网页浏览器
advancements.webdisplays.root.description=内置网页浏览器Mod
advancements.webdisplays.screen.title=互联网是...
advancements.webdisplays.screen.description=制作你第一个屏幕方块
advancements.webdisplays.minepad.title=这可是个革命
advancements.webdisplays.minepad.description=你得到了最新的科技:掌上电脑
advancements.webdisplays.padbreak.title=逆向工程
advancements.webdisplays.padbreak.description=这些东西易碎!不要为了得到升级组件材料而让掌上电脑从高空坠落
advancements.webdisplays.minepad2.title=鸽子
advancements.webdisplays.minepad2.description=制作第二代掌上电脑.看,我知道它有多贵,但这意味着它比任何东西都好,对吧?...对吧?
advancements.webdisplays.linkperipheral.title=这是无线的!
advancements.webdisplays.linkperipheral.description=连接外部设备到屏幕
advancements.webdisplays.keyboardcat.title=可恶的猫
advancements.webdisplays.keyboardcat.description=让一只猫踩过你的键盘
advancements.webdisplays.upgrade.title=不仅只是一个屏幕
advancements.webdisplays.upgrade.description=安装了你的第一个升级组件
advancements.webdisplays.laser.title=不要对着眼睛
advancements.webdisplays.laser.description=制作激光笔!
webdisplays.side.bottom=底部
webdisplays.side.top=顶部
webdisplays.side.north=北方
webdisplays.side.south=南方
webdisplays.side.west=西方
webdisplays.side.east=东方
webdisplays.server.info=如果你需要帮助请输入"help".
webdisplays.server.unknowncmd=未知指令.
webdisplays.server.error=内部错误.请检查日志.
webdisplays.server.error2=内部错误%d.请检查日志.
webdisplays.server.argerror=未识别的参数.
webdisplays.server.queryerr=查询错误,尝试输入"reconnect".
webdisplays.server.errowner=只有拥有者才可以访问.
webdisplays.server.timeout=查询超时.请检查日志.
webdisplays.server.ownername=拥有者名称:%s
webdisplays.server.owneruuid=拥有者UUID:
webdisplays.server.quota=%s/%s已使用
webdisplays.server.fnamearg=丢失文件名参数
webdisplays.server.nameerr=无效的文件名
webdisplays.server.urlcopied=复制网址到你的剪切板.
webdisplays.server.notfound=文件未找到
webdisplays.server.upload.info=选择一个文件来上传
webdisplays.server.upload.parent=[父目录]
webdisplays.server.upload.uploading=上传中...
webdisplays.server.upload.done=完成
webdisplays.server.upload.exists=错误:文件不存在
webdisplays.server.upload.quota=错误:文件过大
webdisplays.server.help.help=显示这段文本
webdisplays.server.help.clear=清空这个屏幕
webdisplays.server.help.exit=离开这个控制台
webdisplays.server.help.access=§k无帮助文件
webdisplays.server.help.owner=显示服务器的拥有者
webdisplays.server.help.quota=显示存储空间
webdisplays.server.help.ls=显示这个服务器的文件
webdisplays.server.help.url=复制一个文件网址到你的剪切板
webdisplays.server.help.upload=打开上传向导
webdisplays.server.help.rm=删除一个文件
webdisplays.server.help.reconnect=重新连接到服务器[调试中]

View File

@ -0,0 +1,11 @@
{
"replace": false,
"values": [
"webdisplays:screen",
"webdisplays:kb_left",
"webdisplays:kb_right",
"webdisplays:rctrl",
"webdisplays:redctrl",
"webdisplays:server"
]
}