Compare commits

...

2 Commits

Author SHA1 Message Date
GiantLuigi4
8df8c6add8 ssr prototyping, no network logic hooked up yet 2023-10-19 17:19:48 -04:00
GiantLuigi4
04ea793021 Update README.md 2023-10-19 11:14:25 -04:00
18 changed files with 1172 additions and 288 deletions

View File

@ -1,10 +1,9 @@
# 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.
This project is a fork of montoyo's [WebDisplays](https://github.com/montoyo/webdisplays) mod from 1.12, updated to work in newer versions, and with some bug fixes and reworking.
WebDisplays requires [MCEF](https://www.curseforge.com/minecraft/mc-mods/mcef), which is using [CinamaMod's fork of 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.
### 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.

View File

@ -83,9 +83,10 @@ dependencies {
// implementation fg.deobf("curse.maven:spark-361579:4381167")
compileOnly fg.deobf("curse.maven:vivecraft-667903:4794431")
implementation fg.deobf("com.cinemamod:mcef-forge:2.0.1-1.20.1") {
transitive = false
}
// implementation fg.deobf("com.cinemamod:mcef-forge:2.0.1-1.20.1") {
// transitive = false
// }
implementation fg.deobf("flatdir.lib:mcef:2.x")
}
sourceSets {

Binary file not shown.

View File

@ -4,6 +4,8 @@
package net.montoyo.wd;
import com.cinemamod.mcef.MCEF;
import com.cinemamod.mcef.MCEFBrowser;
import com.google.gson.Gson;
import net.minecraft.ChatFormatting;
import net.minecraft.advancements.Advancement;
@ -48,10 +50,16 @@ import net.montoyo.wd.init.TabInit;
import net.montoyo.wd.init.TileInit;
import net.montoyo.wd.miniserv.server.Server;
import net.montoyo.wd.net.WDNetworkRegistry;
import net.montoyo.wd.net.client_bound.S2CMessageEnableSSR;
import net.montoyo.wd.net.client_bound.S2CMessageServerInfo;
import net.montoyo.wd.remote.IWDBrowser;
import net.montoyo.wd.remote.client.RemoteBrowser;
import net.montoyo.wd.remote.server.BlankBrowser;
import net.montoyo.wd.remote.server.ServerBrowser;
import net.montoyo.wd.utilities.DistSafety;
import net.montoyo.wd.utilities.Log;
import net.montoyo.wd.utilities.Util;
import org.cef.browser.CefBrowser;
import java.io.*;
import java.net.MalformedURLException;
@ -65,7 +73,7 @@ public class WebDisplays {
public static WebDisplays INSTANCE;
public static SharedProxy PROXY = null;
public static final ResourceLocation ADV_PAD_BREAK = new ResourceLocation("webdisplays", "webdisplays/pad_break");
public static final String BLACKLIST_URL = "mod://webdisplays/blacklisted.html";
public static final Gson GSON = new Gson();
@ -96,29 +104,32 @@ public class WebDisplays {
public long miniservQuota;
public float ytVolume;
public float avDist100;
public float avDist0;
// mod detection
private boolean hasOC;
private boolean hasCC;
private static boolean SSR = false;
public WebDisplays() {
INSTANCE = this;
if(FMLEnvironment.dist.isClient()) {
if (FMLEnvironment.dist.isClient()) {
PROXY = DistSafety.createProxy();
} else {
PROXY = new SharedProxy();
}
if (FMLEnvironment.dist.isClient()) {
// 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);
ClientConfig.init();
}
CommonConfig.init();
//Criterions
criterionPadBreak = new Criterion("pad_break");
criterionUpgradeScreen = new Criterion("upgrade_screen");
@ -134,9 +145,9 @@ public class WebDisplays {
BlockInit.init(bus);
ItemInit.init(bus);
TileInit.init(bus);
PROXY.preInit();
MinecraftForge.EVENT_BUS.register(this);
//Other things
@ -155,7 +166,7 @@ public class WebDisplays {
t.printStackTrace();
}
} */
if (!FMLEnvironment.production) {
ScreenControlRegistry.init();
}
@ -210,7 +221,7 @@ public class WebDisplays {
if (miniservPort != 0) {
Server sv = Server.getInstance();
if(!serverStartedDimensions.contains(level.dimension())) {
if (!serverStartedDimensions.contains(level.dimension())) {
sv.setPort(miniservPort);
sv.setDirectory(new File(worldDir, "wd_filehost"));
sv.start();
@ -222,7 +233,7 @@ public class WebDisplays {
@SubscribeEvent
public void onWorldLeave(LevelEvent.Unload ev) throws IOException {
if(ev.getLevel() instanceof Level level) {
if (ev.getLevel() instanceof Level level) {
if (ev.getLevel().isClientSide() || level.dimension() != Level.OVERWORLD)
return;
Server sw = Server.getInstance();
@ -233,7 +244,7 @@ public class WebDisplays {
@SubscribeEvent
public void onWorldSave(LevelEvent.Save ev) {
if(ev.getLevel() instanceof Level level) {
if (ev.getLevel() instanceof Level level) {
if (ev.getLevel().isClientSide() || level.dimension() != Level.OVERWORLD)
return;
File f = new File(Objects.requireNonNull(ev.getLevel().getServer()).getServerDirectory(), "wd_next.txt");
@ -250,13 +261,13 @@ public class WebDisplays {
@SubscribeEvent
public void onToss(ItemTossEvent ev) {
if(!ev.getEntity().level().isClientSide) {
if (!ev.getEntity().level().isClientSide) {
ItemStack is = ev.getEntity().getItem();
if(is.getItem() == ItemInit.MINEPAD.get()) {
if (is.getItem() == ItemInit.MINEPAD.get()) {
CompoundTag tag = is.getTag();
if(tag == null) {
if (tag == null) {
tag = new CompoundTag();
is.setTag(tag);
}
@ -271,11 +282,11 @@ public class WebDisplays {
@SubscribeEvent
public void onPlayerCraft(PlayerEvent.ItemCraftedEvent ev) {
if(CommonConfig.hardRecipes && ItemInit.isCompCraftItem(ev.getCrafting().getItem()) && (CraftComponent.EXTCARD.makeItemStack().is(ev.getCrafting().getItem()))) {
if((ev.getEntity() instanceof ServerPlayer && !hasPlayerAdvancement((ServerPlayer) ev.getEntity(), ADV_PAD_BREAK)) || PROXY.hasClientPlayerAdvancement(ADV_PAD_BREAK) != HasAdvancement.YES) {
if (CommonConfig.hardRecipes && ItemInit.isCompCraftItem(ev.getCrafting().getItem()) && (CraftComponent.EXTCARD.makeItemStack().is(ev.getCrafting().getItem()))) {
if ((ev.getEntity() instanceof ServerPlayer && !hasPlayerAdvancement((ServerPlayer) ev.getEntity(), ADV_PAD_BREAK)) || PROXY.hasClientPlayerAdvancement(ADV_PAD_BREAK) != HasAdvancement.YES) {
ev.getCrafting().setDamageValue(CraftComponent.BADEXTCARD.ordinal());
if(!ev.getEntity().level().isClientSide)
if (!ev.getEntity().level().isClientSide)
ev.getEntity().level().playSound(null, ev.getEntity().getX(), ev.getEntity().getY(), ev.getEntity().getZ(), SoundEvents.ITEM_BREAK, SoundSource.MASTER, 1.0f, 1.0f);
}
}
@ -288,10 +299,10 @@ public class WebDisplays {
@SubscribeEvent
public void onLogIn(PlayerEvent.PlayerLoggedInEvent ev) {
if(!ev.getEntity().level().isClientSide && ev.getEntity() instanceof ServerPlayer) {
if (!ev.getEntity().level().isClientSide && ev.getEntity() instanceof ServerPlayer) {
IWDDCapability cap = ev.getEntity().getCapability(WDDCapability.Provider.cap, null).orElseThrow(RuntimeException::new);
if(cap.isFirstRun()) {
if (cap.isFirstRun()) {
Util.toast(ev.getEntity(), ChatFormatting.LIGHT_PURPLE, "welcome1");
Util.toast(ev.getEntity(), ChatFormatting.LIGHT_PURPLE, "welcome2");
Util.toast(ev.getEntity(), ChatFormatting.LIGHT_PURPLE, "welcome3");
@ -311,27 +322,27 @@ public class WebDisplays {
@SubscribeEvent
public void onLogOut(PlayerEvent.PlayerLoggedOutEvent ev) {
if(!ev.getEntity().level().isClientSide)
if (!ev.getEntity().level().isClientSide)
Server.getInstance().getClientManager().revokeClientKey(ev.getEntity().getGameProfile().getId());
}
@SubscribeEvent
public void attachEntityCaps(AttachCapabilitiesEvent<Entity> ev) {
if(ev.getObject() instanceof Player)
if (ev.getObject() instanceof Player)
ev.addCapability(CAPABILITY, new WDDCapability.Provider());
}
@SubscribeEvent
public void onPlayerClone(PlayerEvent.Clone ev) {
IWDDCapability src = ev.getOriginal().getCapability(WDDCapability.Provider.cap, null).orElse(new WDDCapability.Factory().call());
IWDDCapability dst = ev.getEntity().getCapability(WDDCapability.Provider.cap, null).orElse(new WDDCapability.Factory().call());
IWDDCapability src = ev.getOriginal().getCapability(WDDCapability.Provider.cap, null).orElse(new WDDCapability.Factory().call());
IWDDCapability dst = ev.getEntity().getCapability(WDDCapability.Provider.cap, null).orElse(new WDDCapability.Factory().call());
if(src == null) {
if (src == null) {
Log.error("src is null");
return;
}
if(dst == null) {
if (dst == null) {
Log.error("dst is null");
return;
}
@ -343,14 +354,14 @@ public class WebDisplays {
public void onServerChat(ServerChatEvent ev) {
String msg = ev.getMessage().getString().replaceAll("\\s+", " ").toLowerCase();
StringBuilder sb = new StringBuilder(msg.length());
for(int i = 0; i < msg.length(); i++) {
for (int i = 0; i < msg.length(); i++) {
char chr = msg.charAt(i);
if(chr != '.' && chr != ',' && chr != ';' && chr != '!' && chr != '?' && chr != ':' && chr != '\'' && chr != '\"' && chr != '`')
if (chr != '.' && chr != ',' && chr != ';' && chr != '!' && chr != '?' && chr != ':' && chr != '\'' && chr != '\"' && chr != '`')
sb.append(chr);
}
if(sb.toString().equals("ironic he could save others from death but not himself")) {
if (sb.toString().equals("ironic he could save others from death but not himself")) {
Player ply = ev.getPlayer();
ply.level().playSound(null, ply.getX(), ply.getY(), ply.getZ(), soundIronic, SoundSource.PLAYERS, 1.0f, 1.0f);
}
@ -358,13 +369,13 @@ public class WebDisplays {
@SubscribeEvent
public void onClientChat(ClientChatEvent ev) {
if(ev.getMessage().equals("!WD render recipes"))
if (ev.getMessage().equals("!WD render recipes"))
PROXY.renderRecipes();
}
private boolean hasPlayerAdvancement(ServerPlayer ply, ResourceLocation rl) {
MinecraftServer server = PROXY.getServer();
if(server == null)
if (server == null)
return false;
Advancement adv = server.getAdvancements().getAdvancement(rl);
@ -385,18 +396,18 @@ public class WebDisplays {
return ret;
}
private static void registerTrigger(Criterion ... criteria) {
for(Criterion c: criteria)
private static void registerTrigger(Criterion... criteria) {
for (Criterion c : criteria)
CriteriaTriggers.register(c);
}
// public static boolean isOpenComputersAvailable() {
// return INSTANCE.hasOC;
// }
// public static boolean isOpenComputersAvailable() {
// return INSTANCE.hasOC;
// }
// public static boolean isComputerCraftAvailable() {
// return INSTANCE.hasCC;
// }
// public static boolean isComputerCraftAvailable() {
// return INSTANCE.hasCC;
// }
public static boolean isSiteBlacklisted(String url) {
try {
@ -404,7 +415,7 @@ public class WebDisplays {
for (String str : CommonConfig.Browser.blacklist)
if (str.equalsIgnoreCase(url2.getHost())) return true;
return false;
} catch(MalformedURLException ex) {
} catch (MalformedURLException ex) {
return false;
}
}
@ -412,5 +423,20 @@ public class WebDisplays {
public static String applyBlacklist(String url) {
return isSiteBlacklisted(url) ? BLACKLIST_URL : url;
}
}
public static boolean isSSR() {
return SSR;
}
public static void markSSR() {
StackTraceElement[] elements = Thread.currentThread().getStackTrace();
StackTraceElement caller = elements[3];
try {
Class<?> c = Class.forName(caller.getClassName());
if (c == S2CMessageEnableSSR.class || c == WebDisplays.class) {
SSR = true;
}
} catch (Throwable err) {
}
}
}

View File

@ -12,6 +12,7 @@ import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.montoyo.wd.entity.TileEntityScreen;
import net.montoyo.wd.remote.IWDBrowser;
import net.montoyo.wd.utilities.Vector3f;
import net.montoyo.wd.utilities.Vector3i;
import org.jetbrains.annotations.NotNull;
@ -47,7 +48,7 @@ public class ScreenRenderer implements BlockEntityRenderer<TileEntityScreen> {
for (int i = 0; i < te.screenCount(); i++) {
TileEntityScreen.Screen scr = te.getScreen(i);
if (scr.browser == null) {
scr.createBrowser(true);
scr.createBrowser(false, true);
}
// TODO: manually backface cull the screens
@ -120,7 +121,7 @@ public class ScreenRenderer implements BlockEntityRenderer<TileEntityScreen> {
//TODO: don't use tesselator
RenderSystem.enableDepthTest();
RenderSystem.setShader(GameRenderer::getPositionTexColorShader);
RenderSystem._setShaderTexture(0, ((MCEFBrowser) scr.browser).getRenderer().getTextureID());
RenderSystem._setShaderTexture(0, ((IWDBrowser) scr.browser).getRenderer().getTextureID());
RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f);
builder.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX_COLOR);
builder.vertex(poseStack.last().pose(), -sw, -sh, 0.505f).uv(0.f, 1.f).color(1.f, 1.f, 1.f, 1.f).endVertex();

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
package net.montoyo.wd.net.client_bound;
import net.minecraftforge.network.NetworkEvent;
import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.net.Packet;
public class S2CMessageEnableSSR extends Packet {
@Override
public void handle(NetworkEvent.Context ctx) {
if (checkClient(ctx)) {
WebDisplays.markSSR();
}
}
}

View File

@ -0,0 +1,69 @@
package net.montoyo.wd.remote;
import com.cinemamod.mcef.MCEF;
import com.cinemamod.mcef.MCEFBrowser;
import net.minecraftforge.fml.loading.FMLEnvironment;
import net.montoyo.wd.WebDisplays;
import net.montoyo.wd.remote.client.RemoteBrowser;
import net.montoyo.wd.remote.server.BlankBrowser;
import net.montoyo.wd.remote.server.ServerBrowser;
import org.cef.browser.CefBrowser;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.UUID;
public class BrowserGen {
private static ArrayList<IWDBrowser> browsers = new ArrayList<>();
private static HashMap<UUID, IRemoteBrowser> clientBrowsers = new HashMap<>();
private static HashMap<UUID, IRemoteBrowser> serverBrowsers = new HashMap<>();
public static IWDBrowser createBrowser(boolean server, String url, boolean transparent) {
CefBrowser browser;
if (!server) {
if (WebDisplays.isSSR()) {
browser = new RemoteBrowser() {
@Override
protected void finalize() throws Throwable {
synchronized (browsers) {
browsers.remove(this);
clientBrowsers.remove(getUUID());
}
super.finalize();
}
};
} else
browser = new WDMCEFBrowser(MCEF.getClient(), url, transparent) {
@Override
public void close() {
synchronized (browsers) {
browsers.remove(this);
super.close();
}
}
};
} else {
if (WebDisplays.isSSR()) {
browser = new ServerBrowser(MCEF.getClient(), url, transparent) {
@Override
public void close() {
browsers.remove(this);
synchronized (browsers) {
serverBrowsers.remove(getUUID());
}
super.close();
}
};
serverBrowsers.put(((IRemoteBrowser) browser).getUUID(), (IRemoteBrowser) browser);
} else return null;
}
browser.setCloseAllowed();
browser.createImmediately();
browsers.add((IWDBrowser) browser);
return (IWDBrowser) browser;
}
public static void onUUIDAcquired(RemoteBrowser browser) {
clientBrowsers.put(((IRemoteBrowser) browser).getUUID(), browser);
}
}

View File

@ -0,0 +1,7 @@
package net.montoyo.wd.remote;
import java.util.UUID;
public interface IRemoteBrowser {
UUID getUUID();
}

View File

@ -0,0 +1,24 @@
package net.montoyo.wd.remote;
import com.cinemamod.mcef.MCEFRenderer;
import com.cinemamod.mcef.listeners.MCEFCursorChangeListener;
public interface IWDBrowser {
void close(boolean b);
void loadURL(String url);
void resize(int y, int x);
void setCursorChangeListener(MCEFCursorChangeListener mcefCursorChangeListener);
MCEFRenderer getRenderer();
void setFocus(boolean b);
void sendMouseRelease(int x, int y, int button);
void sendMouseMove(int x, int y);
void sendMousePress(int x, int y, int button);
}

View File

@ -0,0 +1,275 @@
package net.montoyo.wd.remote;
import com.cinemamod.mcef.MCEF;
import com.cinemamod.mcef.MCEFRenderer;
import com.cinemamod.mcef.listeners.MCEFCursorChangeListener;
import org.cef.CefClient;
import org.cef.browser.CefBrowser;
import org.cef.browser.CefFrame;
import org.cef.callback.CefPdfPrintCallback;
import org.cef.callback.CefRunFileDialogCallback;
import org.cef.callback.CefStringVisitor;
import org.cef.handler.CefDialogHandler;
import org.cef.handler.CefRenderHandler;
import org.cef.handler.CefWindowHandler;
import org.cef.misc.CefPdfPrintSettings;
import org.cef.network.CefRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Vector;
import java.util.concurrent.CompletableFuture;
public abstract class VirtualBrowser implements CefBrowser, CefRenderHandler, CefWindowHandler, IWDBrowser {
String url;
@Override
public void createImmediately() {
}
@Override
public CefClient getClient() {
return MCEF.getClient().getHandle();
}
@Override
public CefRenderHandler getRenderHandler() {
return this;
}
@Override
public CefWindowHandler getWindowHandler() {
return this;
}
@Override
public boolean canGoBack() {
return false;
}
@Override
public void goBack() {
}
@Override
public boolean canGoForward() {
return false;
}
@Override
public void goForward() {
}
@Override
public boolean isLoading() {
return false;
}
@Override
public void reload() {
}
@Override
public void reloadIgnoreCache() {
}
@Override
public void stopLoad() {
}
@Override
public int getIdentifier() {
return 0;
}
@Override
public CefFrame getMainFrame() {
return null;
}
@Override
public CefFrame getFocusedFrame() {
return null;
}
@Override
public CefFrame getFrame(long l) {
return null;
}
@Override
public CefFrame getFrame(String s) {
return null;
}
@Override
public Vector<Long> getFrameIdentifiers() {
return null;
}
@Override
public Vector<String> getFrameNames() {
return null;
}
@Override
public int getFrameCount() {
return 0;
}
@Override
public boolean isPopup() {
return false;
}
@Override
public boolean hasDocument() {
return false;
}
@Override
public void viewSource() {
}
@Override
public void getSource(CefStringVisitor cefStringVisitor) {
}
@Override
public void getText(CefStringVisitor cefStringVisitor) {
}
@Override
public void loadRequest(CefRequest cefRequest) {
}
@Override
public void loadURL(String s) {
this.url = url;
}
@Override
public void executeJavaScript(String s, String s1, int i) {
}
@Override
public String getURL() {
return url;
}
@Override
public void close(boolean b) {
}
@Override
public void setCloseAllowed() {
}
@Override
public boolean doClose() {
return true;
}
@Override
public void onBeforeClose() {
}
@Override
public void setFocus(boolean b) {
}
@Override
public void setWindowVisibility(boolean b) {
}
@Override
public double getZoomLevel() {
return 0;
}
@Override
public void setZoomLevel(double v) {
}
@Override
public void runFileDialog(CefDialogHandler.FileDialogMode fileDialogMode, String s, String s1, Vector<String> vector, int i, CefRunFileDialogCallback cefRunFileDialogCallback) {
}
@Override
public void startDownload(String s) {
}
@Override
public void print() {
}
@Override
public void printToPDF(String s, CefPdfPrintSettings cefPdfPrintSettings, CefPdfPrintCallback cefPdfPrintCallback) {
}
@Override
public void find(String s, boolean b, boolean b1, boolean b2) {
}
@Override
public void stopFinding(boolean b) {
}
@Override
public CefBrowser getDevTools() {
return this;
}
@Override
public CefBrowser getDevTools(Point point) {
return this;
}
@Override
public void replaceMisspelling(String s) {
}
@Override
public CompletableFuture<BufferedImage> createScreenshot(boolean b) {
return null;
}
@Override
public void resize(int y, int x) {
}
@Override
public void setCursorChangeListener(MCEFCursorChangeListener mcefCursorChangeListener) {
}
@Override
public MCEFRenderer getRenderer() {
return null;
}
}

View File

@ -0,0 +1,10 @@
package net.montoyo.wd.remote;
import com.cinemamod.mcef.MCEFBrowser;
import com.cinemamod.mcef.MCEFClient;
public class WDMCEFBrowser extends MCEFBrowser implements IWDBrowser {
public WDMCEFBrowser(MCEFClient client, String url, boolean transparent) {
super(client, url, transparent);
}
}

View File

@ -0,0 +1,53 @@
package net.montoyo.wd.remote.client;
import net.minecraft.network.FriendlyByteBuf;
import java.nio.ByteBuffer;
public class ImageReader {
ByteBuffer target;
public ImageReader(ByteBuffer target) {
this.target = target;
}
public void read(ByteBuffer buf, int bWidth) {
int lim = buf.limit();
int count = buf.getInt();
for (int i = 0; i < count; i++) {
int x = buf.getInt();
int y = buf.getInt();
int width = buf.getInt();
int height = buf.getInt();
for (int y1 = 0; y1 < height; y1++) {
buf.limit(buf.position() + width * 4);
target.position(
x * 4 + (y + y1) * 4 * bWidth
);
target.put(buf);
buf.limit(lim);
}
}
}
public void read(FriendlyByteBuf buf, int bWidth) {
int count = buf.readInt();
for (int i = 0; i < count; i++) {
int x = buf.readInt();
int y = buf.readInt();
int width = buf.readInt();
int height = buf.readInt();
for (int y1 = 0; y1 < height; y1++) {
ByteBuffer buf1 = buf.internalNioBuffer(buf.readerIndex(), width * 4);
target.position(
x * 4 + (y + y1) * 4 * bWidth
);
target.put(buf1);
buf.skipBytes(width * 4);
}
}
}
}

View File

@ -0,0 +1,137 @@
package net.montoyo.wd.remote.client;
import com.cinemamod.mcef.MCEFRenderer;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Minecraft;
import net.montoyo.wd.remote.IRemoteBrowser;
import net.montoyo.wd.remote.IWDBrowser;
import net.montoyo.wd.remote.VirtualBrowser;
import org.cef.browser.CefBrowser;
import org.cef.callback.CefDragData;
import org.cef.handler.CefScreenInfo;
import java.awt.*;
import java.lang.reflect.Constructor;
import java.nio.ByteBuffer;
import java.util.UUID;
import static org.lwjgl.opengl.GL11C.*;
public class RemoteBrowser extends VirtualBrowser implements IRemoteBrowser, IWDBrowser {
UUID myId;
MCEFRenderer renderer;
public RemoteBrowser() {
try {
Constructor<MCEFRenderer> ctor = MCEFRenderer.class.getDeclaredConstructor(boolean.class);
ctor.setAccessible(true);
renderer = ctor.newInstance(false);
Minecraft.getInstance().submit(renderer::initialize);
} catch (Throwable err) {
throw new RuntimeException(err);
}
}
@Override
public MCEFRenderer getRenderer() {
return renderer;
}
@Override
public UUID getUUID() {
return myId;
}
public void setUUID(UUID uuid) {
myId = uuid;
}
@Override
public Rectangle getViewRect(CefBrowser cefBrowser) {
return null;
}
@Override
public boolean getScreenInfo(CefBrowser cefBrowser, CefScreenInfo cefScreenInfo) {
return false;
}
@Override
public Point getScreenPoint(CefBrowser cefBrowser, Point point) {
return null;
}
@Override
public void onPopupShow(CefBrowser cefBrowser, boolean b) {
}
@Override
public void onPopupSize(CefBrowser cefBrowser, Rectangle rectangle) {
}
int lastWidth, lastHeight;
@Override
public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, ByteBuffer buffer, int width, int height) {
if (width != lastWidth || height != lastHeight) {
renderer.onPaint(buffer, width, height);
lastWidth = width;
lastHeight = height;
} else {
if (renderer.getTextureID() == 0) return;
RenderSystem.bindTexture(renderer.getTextureID());
if (renderer.isTransparent()) RenderSystem.enableBlend();
RenderSystem.pixelStore(GL_UNPACK_ROW_LENGTH, width);
for (Rectangle dirtyRect : dirtyRects) {
GlStateManager._pixelStore(GL_UNPACK_SKIP_PIXELS, dirtyRect.x);
GlStateManager._pixelStore(GL_UNPACK_SKIP_ROWS, dirtyRect.y);
renderer.onPaint(buffer, dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height);
}
}
}
@Override
public boolean onCursorChange(CefBrowser cefBrowser, int i) {
return false;
}
@Override
public boolean startDragging(CefBrowser cefBrowser, CefDragData cefDragData, int i, int i1, int i2) {
return false;
}
@Override
public void updateDragCursor(CefBrowser cefBrowser, int i) {
}
@Override
public Rectangle getRect(CefBrowser cefBrowser) {
return null;
}
@Override
public void onMouseEvent(CefBrowser cefBrowser, int i, int i1, int i2, int i3, int i4) {
}
@Override
public void sendMouseRelease(int x, int y, int button) {
}
@Override
public void sendMouseMove(int x, int y) {
}
@Override
public void sendMousePress(int x, int y, int button) {
}
}

View File

@ -0,0 +1,78 @@
package net.montoyo.wd.remote.server;
import net.montoyo.wd.remote.VirtualBrowser;
import org.cef.browser.CefBrowser;
import org.cef.callback.CefDragData;
import org.cef.handler.CefScreenInfo;
import java.awt.*;
import java.nio.ByteBuffer;
public class BlankBrowser extends VirtualBrowser {
@Override
public Rectangle getViewRect(CefBrowser cefBrowser) {
return null;
}
@Override
public boolean getScreenInfo(CefBrowser cefBrowser, CefScreenInfo cefScreenInfo) {
return false;
}
@Override
public Point getScreenPoint(CefBrowser cefBrowser, Point point) {
return null;
}
@Override
public void onPopupShow(CefBrowser cefBrowser, boolean b) {
}
@Override
public void onPopupSize(CefBrowser cefBrowser, Rectangle rectangle) {
}
@Override
public void onPaint(CefBrowser cefBrowser, boolean b, Rectangle[] rectangles, ByteBuffer byteBuffer, int i, int i1) {
}
@Override
public boolean onCursorChange(CefBrowser cefBrowser, int i) {
return false;
}
@Override
public boolean startDragging(CefBrowser cefBrowser, CefDragData cefDragData, int i, int i1, int i2) {
return false;
}
@Override
public void updateDragCursor(CefBrowser cefBrowser, int i) {
}
@Override
public Rectangle getRect(CefBrowser cefBrowser) {
return null;
}
@Override
public void onMouseEvent(CefBrowser cefBrowser, int i, int i1, int i2, int i3, int i4) {
}
@Override
public void sendMouseRelease(int x, int y, int button) {
}
@Override
public void sendMouseMove(int x, int y) {
}
@Override
public void sendMousePress(int x, int y, int button) {
}
}

View File

@ -0,0 +1,61 @@
package net.montoyo.wd.remote.server;
import com.cinemamod.mcef.MCEFHeadlessBrowser;
import com.cinemamod.mcef.MCEFClient;
import com.cinemamod.mcef.MCEFRenderer;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.systems.RenderSystem;
import net.montoyo.wd.remote.IRemoteBrowser;
import net.montoyo.wd.remote.IWDBrowser;
import org.cef.browser.CefBrowser;
import java.awt.*;
import java.nio.ByteBuffer;
import java.util.UUID;
public class ServerBrowser extends MCEFHeadlessBrowser implements IRemoteBrowser, IWDBrowser {
private int lastWidth = 0;
private int lastHeight = 0;
UUID myId;
ServerRenderer renderer = new ServerRenderer();
public ServerBrowser(MCEFClient client, String url, boolean transparent) {
super(client, url, transparent);
this.myId = UUID.randomUUID();
}
@Override
public UUID getUUID() {
return myId;
}
ByteBuffer graphics;
void store(ByteBuffer srcBuffer, ByteBuffer dstBuffer, Rectangle dirty, int width, int height) {
for (int y = dirty.y; y < dirty.height + dirty.y; y++) {
dstBuffer.position((y * width + dirty.x) * 4);
srcBuffer.position((y * width + dirty.x) * 4);
srcBuffer.limit(dirty.width * 4 + (y * width + dirty.x) * 4);
dstBuffer.put(srcBuffer);
srcBuffer.position(0).limit(srcBuffer.capacity());
}
dstBuffer.position(0).limit(dstBuffer.capacity());
}
@Override
public void onPaint(CefBrowser browser, boolean popup, Rectangle[] dirtyRects, ByteBuffer buffer, int width, int height) {
if ((width != this.lastWidth && height != this.lastHeight)) {
renderer.size(width, height);
renderer.put(buffer, new Rectangle[]{
new Rectangle(0, 0, width, height)
}, width);
this.lastWidth = width;
this.lastHeight = height;
} else {
renderer.put(buffer, dirtyRects, width);
}
}
}

View File

@ -0,0 +1,57 @@
package net.montoyo.wd.remote.server;
import java.awt.*;
import java.nio.ByteBuffer;
public class ServerRenderer {
ByteBuffer contentChange;
public ServerRenderer() {
}
public void size(int width, int height) {
contentChange = ByteBuffer.allocate(width * height * 4);
}
protected void checkGrow(int cap) {
if (contentChange.position() + cap > contentChange.capacity()) {
ByteBuffer copy = ByteBuffer.allocate((int) (contentChange.capacity() * 1.5));
copy.position(0);
contentChange.flip();
copy.put(contentChange);
contentChange = copy;
}
}
public void put(ByteBuffer buffer, Rectangle[] dirtyRects, int width) {
contentChange.position(0);
contentChange.limit(contentChange.capacity());
checkGrow(4);
contentChange.putInt(dirtyRects.length);
for (Rectangle dirtyRect : dirtyRects) {
checkGrow(4 * 4);
contentChange.putInt(dirtyRect.x);
contentChange.putInt(dirtyRect.y);
contentChange.putInt(dirtyRect.width);
contentChange.putInt(dirtyRect.height);
for (int y = dirtyRect.y; y < dirtyRect.height + dirtyRect.y; y++) {
checkGrow(dirtyRect.width * 8);
buffer.position(
dirtyRect.x * 4 +
(y * width * 4)
).limit(
(dirtyRect.x + dirtyRect.width) * 4 +
(y * width * 4)
);
contentChange.put(buffer);
buffer.position(0).limit(buffer.capacity());
}
}
contentChange.flip();
}
public ByteBuffer get() {
return contentChange;
}
}

View File

@ -0,0 +1,43 @@
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.EmptyByteBuf;
import net.minecraft.network.FriendlyByteBuf;
import net.montoyo.wd.remote.client.ImageReader;
import net.montoyo.wd.remote.server.ServerRenderer;
import java.awt.*;
import java.nio.ByteBuffer;
import java.util.Random;
public class NetworkedBrowserTest {
public static void main(String[] args) {
int w = 45;
ByteBuffer src = ByteBuffer.allocate((w * w) * 4);
Random rng = new Random();
byte[] bbyte = new byte[1];
for (int i = 0; i < (w * w) * 4; i++) {
rng.nextBytes(bbyte);
src.put(bbyte[0]);
}
ServerRenderer renderer = new ServerRenderer();
renderer.size(w, w);
renderer.put(src, new Rectangle[]{
new Rectangle(0, 0, w, w)
}, w);
ByteBuffer buffer = renderer.get();
ByteBuffer dst = ByteBuffer.allocate((w * w) * 4);
ImageReader reader = new ImageReader(dst);
reader.read(buffer, w);
dst.position(0).limit(dst.capacity());
src.position(0).limit(src.capacity());
for (int i = 0; i < src.capacity(); i++) {
if (src.get(i) != dst.get(i)) System.err.println("Mismatch at " + i);
// else System.out.println(" Correct at " + i);
}
}
}