First version

This commit is contained in:
Michael Wain 2024-07-28 00:19:44 +03:00
parent f314a4ccf8
commit 8b0a15413c
26 changed files with 932 additions and 66 deletions

View File

@ -12,5 +12,5 @@ halplibe_version=3.5.4
# Mod # Mod
mod_version=1.0.0 mod_version=1.0.0
mod_group=turniplabs mod_group=alterdekim
mod_name=examplemod mod_name=offlineskin

View File

@ -0,0 +1,7 @@
package alterwain.offlineskin;
import net.minecraft.client.render.ImageParser;
public interface ForceDownloadHandler {
boolean offlineSkinChanger$forceLoadDownloadableTexture(String url, String localTexture, ImageParser imageParser);
}

View File

@ -0,0 +1,67 @@
package alterwain.offlineskin;
import net.minecraft.client.gui.GuiButton;
import net.minecraft.client.gui.GuiScreen;
import net.minecraft.core.lang.I18n;
import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import static alterwain.offlineskin.OfflineSkinMod.configPath;
public class GuiSkinChanger extends GuiScreen {
private GuiButton buttonLoadSkin;
private GuiButton buttonLoadCape;
private GuiButton buttonClose;
public GuiSkinChanger(GuiScreen parent) {
super(parent);
}
@Override
public void init() {
I18n stringtranslate = I18n.getInstance();
this.controlList.clear();
this.controlList.add(this.buttonLoadSkin = new GuiButton(0, this.width / 2 - 100, this.height / 4 + 96 + 12, stringtranslate.translateKey("gui.options.page.edit_skin.button.load_skin")));
this.controlList.add(this.buttonLoadCape = new GuiButton(1, this.width / 2 - 100, this.height / 4 - 10 + 50 + 18 + 20 + 4, 200, 20, stringtranslate.translateKey("gui.options.page.edit_skin.button.load_cape")));
this.controlList.add(this.buttonClose = new GuiButton(2, this.width / 2 - 100, this.height / 4 + 120 + 12, stringtranslate.translateKey("gui.options.page.edit_skin.button.close")));
}
@Override
protected void buttonPressed(GuiButton button) {
try {
if (button.enabled) {
if (button.id == 2) {
this.mc.displayGuiScreen(this.getParentScreen());
} else if (button.id == 0) { // skin
File skin = OfflineSkinMod.chooseFile();
if (skin != null) {
OfflineSkinMod.skinImage = ImageIO.read(skin);
Files.copy(skin.toPath(), new File(configPath, "skin.png").toPath(), StandardCopyOption.REPLACE_EXISTING);
}
} else if (button.id == 1) { // cape
File cape = OfflineSkinMod.chooseFile();
if (cape != null) {
OfflineSkinMod.capeImage = ImageIO.read(cape);
Files.copy(cape.toPath(), new File(configPath, "cape.png").toPath(), StandardCopyOption.REPLACE_EXISTING);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void drawScreen(int mouseX, int mouseY, float partialTick) {
I18n stringtranslate = I18n.getInstance();
this.drawDefaultBackground();
this.drawStringCentered(this.fontRenderer, stringtranslate.translateKey("gui.options.page.edit_skin.label.title"), this.width / 2, this.height / 4 - 60 + 20, 16777215);
super.drawScreen(mouseX, mouseY, partialTick);
}
}

View File

@ -0,0 +1,97 @@
package alterwain.offlineskin;
import net.fabricmc.api.ModInitializer;
import net.minecraft.core.Global;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import turniplabs.halplibe.util.GameStartEntrypoint;
import turniplabs.halplibe.util.RecipeEntrypoint;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
public class OfflineSkinMod implements ModInitializer, GameStartEntrypoint, RecipeEntrypoint {
public static final String MOD_ID = "offlineskin";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
public static File configPath = new File(Global.accessor.getMinecraftDir(), "config/offlineskin");
public static BufferedImage skinImage;
public static BufferedImage capeImage;
public static final Map<String, SkinConfig> skins = new HashMap<>();
@Override
public void onInitialize() {
LOGGER.info("ExampleMod initialized.");
}
@Override
public void beforeGameStart() {
configPath.mkdirs();
try {
if (new File(configPath, "skin.png").exists()) {
skinImage = ImageIO.read(new File(configPath, "skin.png"));
}
if (new File(configPath, "cape.png").exists()) {
capeImage = ImageIO.read(new File(configPath, "cape.png"));
}
} catch (IOException e) {
LOGGER.error(e.getMessage());
}
}
@Override
public void afterGameStart() {
}
@Override
public void onRecipesReady() {
}
@Override
public void initNamespaces() {
}
public static File chooseFile() {
JFileChooser fileChooser = new JFileChooser();
FileFilter filter = new FileNameExtensionFilter("PNG File","png");
fileChooser.addChoosableFileFilter(filter);
int returnValue = fileChooser.showOpenDialog(null);
if (returnValue == JFileChooser.APPROVE_OPTION) {
return fileChooser.getSelectedFile();
}
return null;
}
public static BufferedImage bytesToImage(byte[] b) {
try {
ByteArrayInputStream is = new ByteArrayInputStream(b);
return ImageIO.read(is);
} catch (IOException e) {
return null; // TODO: read default skin from resources.
}
}
public static byte[] imageToBytes(BufferedImage image) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
ImageIO.write(image, "png", baos);
} catch (IOException e) {
return new byte[1];
}
return baos.toByteArray();
}
}

View File

@ -0,0 +1,28 @@
package alterwain.offlineskin;
import alterwain.offlineskin.packet.Packet246SkinSet;
import net.minecraft.server.entity.player.EntityPlayerMP;
public class SendInfo extends Thread {
private EntityPlayerMP player;
public SendInfo(EntityPlayerMP player) {
this.player = player;
}
@Override
public void run() {
try {
Thread.sleep(3000);
OfflineSkinMod.skins.keySet().stream().filter(k -> !k.equals(player.username))
.forEach(k -> player.playerNetServerHandler.sendPacket(new Packet246SkinSet(k,
OfflineSkinMod.imageToBytes(OfflineSkinMod.skins.get(k).getSkin()),
OfflineSkinMod.imageToBytes(OfflineSkinMod.skins.get(k).getCape()),
OfflineSkinMod.skins.get(k).getModelType()
)));
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,29 @@
package alterwain.offlineskin;
import net.minecraft.client.render.EntityRenderDispatcher;
import net.minecraft.client.render.PlayerSkinParser;
public class SendSet extends Thread {
private final String username;
private final boolean isCape;
public SendSet(String username, boolean isCape) {
this.username = username;
this.isCape = isCape;
}
@Override
public void run() {
try {
Thread.sleep(2500);
if( isCape ) {
((ForceDownloadHandler) EntityRenderDispatcher.instance.renderEngine).offlineSkinChanger$forceLoadDownloadableTexture("offlineCapeLocal:" + this.username, null, null);
return;
}
((ForceDownloadHandler) EntityRenderDispatcher.instance.renderEngine).offlineSkinChanger$forceLoadDownloadableTexture("offlineSkinLocal:"+this.username, null, PlayerSkinParser.instance);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -0,0 +1,27 @@
package alterwain.offlineskin;
import java.awt.image.BufferedImage;
public class SkinConfig {
private BufferedImage skin;
private BufferedImage cape;
private Boolean modelType;
public SkinConfig(BufferedImage skin, BufferedImage cape, Boolean modelType) {
this.skin = skin;
this.cape = cape;
this.modelType = modelType;
}
public BufferedImage getSkin() {
return skin;
}
public BufferedImage getCape() {
return cape;
}
public Boolean getModelType() {
return modelType;
}
}

View File

@ -0,0 +1,8 @@
package alterwain.offlineskin;
import alterwain.offlineskin.packet.Packet244SkinRequest;
public interface SkinRequestHandler {
void offlineSkinChanger$handleSkinRequest(Packet244SkinRequest request);
}

View File

@ -0,0 +1,7 @@
package alterwain.offlineskin;
import alterwain.offlineskin.packet.Packet245SkinResponse;
public interface SkinResponseHandler {
void offlineSkinChanger$handleSkinResponse(Packet245SkinResponse response);
}

View File

@ -0,0 +1,28 @@
package alterwain.offlineskin.mixin;
import alterwain.offlineskin.OfflineSkinMod;
import alterwain.offlineskin.packet.Packet244SkinRequest;
import alterwain.offlineskin.packet.Packet245SkinResponse;
import alterwain.offlineskin.SkinRequestHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.net.handler.NetClientHandler;
import net.minecraft.core.net.NetworkManager;
import net.minecraft.core.net.handler.NetHandler;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(NetClientHandler.class)
public abstract class NetClientHandlerMixin extends NetHandler implements SkinRequestHandler {
@Shadow @Final private NetworkManager netManager;
@Shadow @Final private Minecraft mc;
@Override
public void offlineSkinChanger$handleSkinRequest(Packet244SkinRequest request) {
this.netManager.addToSendQueue(new Packet245SkinResponse(mc.thePlayer.username,
OfflineSkinMod.imageToBytes(OfflineSkinMod.skinImage),
OfflineSkinMod.imageToBytes(OfflineSkinMod.capeImage),
false));
}
}

View File

@ -0,0 +1,32 @@
package alterwain.offlineskin.mixin;
import alterwain.offlineskin.OfflineSkinMod;
import alterwain.offlineskin.SkinConfig;
import alterwain.offlineskin.SkinResponseHandler;
import alterwain.offlineskin.packet.Packet245SkinResponse;
import alterwain.offlineskin.packet.Packet246SkinSet;
import net.minecraft.server.net.handler.NetServerHandler;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(NetServerHandler.class)
public abstract class NetServerHandlerMixin extends net.minecraft.core.net.handler.NetHandler implements net.minecraft.core.net.ICommandListener,
SkinResponseHandler {
@Shadow
private net.minecraft.server.MinecraftServer mcServer;
@Override
public void offlineSkinChanger$handleSkinResponse(Packet245SkinResponse response) {
OfflineSkinMod.skins.put(response.getUsername(), new SkinConfig(
OfflineSkinMod.bytesToImage(response.getSkin()),
OfflineSkinMod.bytesToImage(response.getCape()),
response.isModelType()
));
for( int i = 0; i < this.mcServer.playerList.playerEntities.size(); i++ ) {
this.mcServer.playerList.playerEntities.get(i)
.playerNetServerHandler
.sendPacket(new Packet246SkinSet(response.getUsername(), response.getSkin(), response.getCape(), response.isModelType()));
}
}
}

View File

@ -0,0 +1,28 @@
package alterwain.offlineskin.mixin;
import alterwain.offlineskin.GuiSkinChanger;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.options.components.*;
import net.minecraft.client.gui.options.data.OptionsPages;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(OptionsPages.class)
public abstract class OptionsPagesMixin {
@Final
@Shadow
@Mutable
private static Minecraft mc;
@Inject(method = "<clinit>", at = @At("TAIL"))
private static void modifyGeneralScreen(CallbackInfo ci) {
OptionsPages.GENERAL.withComponent(new ShortcutComponent("gui.options.page.general.button.edit_skin", () -> {
mc.displayGuiScreen(new GuiSkinChanger(mc.currentScreen));
}));
}
}

View File

@ -0,0 +1,21 @@
package alterwain.offlineskin.mixin;
import alterwain.offlineskin.packet.Packet244SkinRequest;
import alterwain.offlineskin.packet.Packet245SkinResponse;
import alterwain.offlineskin.packet.Packet246SkinSet;
import net.minecraft.core.net.packet.Packet;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(Packet.class)
public abstract class PacketMixin {
@Inject(method = "<clinit>", at = @At("TAIL"))
private static void modifyPacketsTable(CallbackInfo ci) {
Packet.addIdClassMapping(244, true, false, Packet244SkinRequest.class);
Packet.addIdClassMapping(245, false, true, Packet245SkinResponse.class);
Packet.addIdClassMapping(246, true, false, Packet246SkinSet.class);
}
}

View File

@ -0,0 +1,30 @@
package alterwain.offlineskin.mixin;
import alterwain.offlineskin.OfflineSkinMod;
import alterwain.offlineskin.SendInfo;
import alterwain.offlineskin.packet.Packet244SkinRequest;
import alterwain.offlineskin.packet.Packet246SkinSet;
import net.minecraft.server.entity.player.EntityPlayerMP;
import net.minecraft.server.player.PlayerManager;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(PlayerManager.class)
public abstract class PlayerManagerMixin {
@Shadow(remap = false)
@Final
public List<EntityPlayerMP> players;
@Inject(method = "addPlayer", at = @At("HEAD"), remap = false)
private void onAddPlayer(EntityPlayerMP player, CallbackInfo ci) {
player.playerNetServerHandler.sendPacket(new Packet244SkinRequest(true, true, false));
new SendInfo(player).start();
}
}

View File

@ -0,0 +1,168 @@
package alterwain.offlineskin.mixin;
import net.minecraft.client.render.ImageParser;
import net.minecraft.client.render.PlayerSkinParser;
import net.minecraft.client.render.block.model.BlockModel;
import net.minecraft.client.render.block.model.BlockModelDispatcher;
import net.minecraft.client.render.entity.LivingRenderer;
import net.minecraft.client.render.entity.PlayerRenderer;
import net.minecraft.client.render.model.ModelBase;
import net.minecraft.client.render.model.ModelBiped;
import net.minecraft.client.render.model.ModelPlayer;
import net.minecraft.core.block.Block;
import net.minecraft.core.entity.player.EntityPlayer;
import net.minecraft.core.item.Item;
import net.minecraft.core.item.ItemStack;
import net.minecraft.core.util.helper.MathHelper;
import org.lwjgl.opengl.GL11;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(PlayerRenderer.class)
public abstract class PlayerRendererMixin extends LivingRenderer<EntityPlayer> {
@Shadow(remap = false)
private ModelBiped modelBipedMain;
@Final
@Shadow(remap = false)
private ModelPlayer modelSlim;
@Final
@Shadow(remap = false)
private ModelPlayer modelThick;
public PlayerRendererMixin(ModelBase model, float shadowSize) {
super(model, shadowSize);
}
@Override
public void loadEntityTexture(EntityPlayer entity) {
this.loadDownloadableTexture("offlineSkinLocal:"+entity.username, entity.getEntityTexture(), PlayerSkinParser.instance);
}
public void drawFirstPersonHand(EntityPlayer player) {
player.skinURL = "offlineSkinLocal:"+player.username;
this.mainModel = player.slimModel ? this.modelSlim : this.modelThick;
this.modelBipedMain = player.slimModel ? this.modelSlim : this.modelThick;
this.modelBipedMain.onGround = 0.0F;
this.modelBipedMain.isRiding = false;
this.modelBipedMain.setRotationAngles(0.0F, 0.0F, 0.0F, 0.0F, 0.0F, 0.0625F);
this.modelBipedMain.bipedRightArm.render(0.0625F);
if (this.modelBipedMain instanceof ModelPlayer) {
((ModelPlayer)this.modelBipedMain).bipedRightArmOverlay.render(0.0625F);
}
}
protected void renderSpecials(EntityPlayer entity, float f) {
ItemStack itemstack = entity.inventory.armorItemInSlot(3);
if (itemstack != null && itemstack.getItem().id < Block.blocksList.length) {
GL11.glPushMatrix();
this.modelBipedMain.bipedHead.postRender(0.0625F);
if (((BlockModel) BlockModelDispatcher.getInstance().getDispatch(Block.blocksList[itemstack.itemID])).shouldItemRender3d()) {
float f1 = 0.625F;
GL11.glTranslatef(0.0F, -0.25F, 0.0F);
GL11.glRotatef(180.0F, 0.0F, 1.0F, 0.0F);
GL11.glScalef(f1, -f1, f1);
}
this.renderDispatcher.itemRenderer.renderItem(entity, itemstack);
GL11.glPopMatrix();
}
boolean renderCape = this.loadDownloadableTexture("offlineCapeLocal:"+entity.username, (String)null, (ImageParser)null);
if (renderCape) {
GL11.glPushMatrix();
GL11.glTranslatef(0.0F, 0.0F, 0.125F);
double d = entity.field_20066_r + (entity.field_20063_u - entity.field_20066_r) * (double)f - (entity.xo + (entity.x - entity.xo) * (double)f);
double d1 = entity.field_20065_s + (entity.field_20062_v - entity.field_20065_s) * (double)f - (entity.yo + (entity.y - entity.yo) * (double)f);
double d2 = entity.field_20064_t + (entity.field_20061_w - entity.field_20064_t) * (double)f - (entity.zo + (entity.z - entity.zo) * (double)f);
float f8 = entity.prevRenderYawOffset + (entity.renderYawOffset - entity.prevRenderYawOffset) * f;
double d3 = (double) MathHelper.sin(f8 * 3.141593F / 180.0F);
double d4 = (double)(-MathHelper.cos(f8 * 3.141593F / 180.0F));
float f9 = (float)d1 * 10.0F;
if (f9 < -6.0F) {
f9 = -6.0F;
}
if (f9 > 32.0F) {
f9 = 32.0F;
}
float f10 = (float)(d * d3 + d2 * d4) * 100.0F;
float f11 = (float)(d * d4 - d2 * d3) * 100.0F;
if (f10 < 0.0F) {
f10 = 0.0F;
}
float f12 = entity.field_775_e + (entity.field_774_f - entity.field_775_e) * f;
f9 += MathHelper.sin((entity.walkDistO + (entity.walkDist - entity.walkDistO) * f) * 6.0F) * 32.0F * f12;
if (entity.isSneaking()) {
f9 += 25.0F;
}
GL11.glRotatef(6.0F + f10 / 2.0F + f9, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(f11 / 2.0F, 0.0F, 0.0F, 1.0F);
GL11.glRotatef(-f11 / 2.0F, 0.0F, 1.0F, 0.0F);
GL11.glRotatef(180.0F, 0.0F, 1.0F, 0.0F);
this.modelBipedMain.renderCloak(0.0625F);
GL11.glPopMatrix();
}
ItemStack itemstack1 = entity.inventory.getCurrentItem();
if (itemstack1 != null) {
GL11.glPushMatrix();
this.modelBipedMain.bipedRightArm.postRender(0.0625F);
GL11.glTranslatef(-0.0625F, 0.4375F, 0.0625F);
if (entity.fishEntity != null) {
itemstack1 = new ItemStack(Item.stick);
}
float f4;
if (itemstack1.itemID < Block.blocksList.length && ((BlockModel)BlockModelDispatcher.getInstance().getDispatch(Block.blocksList[itemstack1.itemID])).shouldItemRender3d()) {
f4 = 0.5F;
GL11.glTranslatef(0.0F, 0.1875F, -0.3125F);
f4 *= 0.75F;
GL11.glRotatef(20.0F, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F);
GL11.glScalef(f4, -f4, f4);
} else if (itemstack1.itemID == Item.toolBow.id) {
f4 = 0.625F;
GL11.glTranslatef(0.0F, 0.125F, 0.3125F);
GL11.glRotatef(-20.0F, 0.0F, 1.0F, 0.0F);
GL11.glScalef(f4, -f4, f4);
GL11.glRotatef(-100.0F, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F);
} else if (Item.itemsList[itemstack1.itemID].isFull3D()) {
f4 = 0.625F;
if (Item.itemsList[itemstack1.itemID].shouldRotateAroundWhenRendering()) {
GL11.glRotatef(180.0F, 0.0F, 0.0F, 1.0F);
GL11.glTranslatef(0.0F, -0.125F, 0.0F);
}
if (Item.itemsList[itemstack1.itemID].shouldPointInFrontOfPlayer()) {
GL11.glRotatef(-20.0F, 0.0F, 1.0F, 0.0F);
GL11.glTranslatef(0.0F, -0.125F, 0.0F);
}
GL11.glTranslatef(0.0F, 0.1875F, 0.0F);
GL11.glScalef(f4, -f4, f4);
GL11.glRotatef(-100.0F, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(45.0F, 0.0F, 1.0F, 0.0F);
} else {
f4 = 0.375F;
GL11.glTranslatef(0.25F, 0.1875F, -0.1875F);
GL11.glScalef(f4, f4, f4);
GL11.glRotatef(60.0F, 0.0F, 0.0F, 1.0F);
GL11.glRotatef(-90.0F, 1.0F, 0.0F, 0.0F);
GL11.glRotatef(20.0F, 0.0F, 0.0F, 1.0F);
}
this.renderDispatcher.itemRenderer.renderItem(entity, itemstack1);
GL11.glPopMatrix();
}
}
}

View File

@ -0,0 +1,107 @@
package alterwain.offlineskin.mixin;
import alterwain.offlineskin.ForceDownloadHandler;
import alterwain.offlineskin.OfflineSkinMod;
import net.minecraft.client.render.DownloadedTexture;
import net.minecraft.client.render.ImageParser;
import net.minecraft.client.render.RenderEngine;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.awt.image.BufferedImage;
import java.util.Map;
@Mixin(RenderEngine.class)
public abstract class RenderEngineMixin implements ForceDownloadHandler {
@Shadow
private Map<String, DownloadedTexture> downloadedTextures;
@Shadow
public abstract int allocateAndSetupTexture(BufferedImage bufferedimage);
@Shadow
public abstract void bindTexture(int i);
@Shadow
public abstract int getTexture(String name);
@Override
public boolean offlineSkinChanger$forceLoadDownloadableTexture(String url, String localTexture, ImageParser imageParser) {
DownloadedTexture texture = new DownloadedTexture(null, imageParser);
if( url.startsWith("offlineSkinLocal") ) {
if( OfflineSkinMod.skins.containsKey(url.substring(17)) ) {
texture.image = OfflineSkinMod.skins.get(url.substring(17)).getSkin();
} else {
texture.image = OfflineSkinMod.skinImage;
}
} else if( url.startsWith("offlineCapeLocal") ) {
if( OfflineSkinMod.skins.containsKey(url.substring(17))) {
texture.image = OfflineSkinMod.skins.get(url.substring(17)).getCape();
} else {
texture.image = OfflineSkinMod.capeImage;
}
} else {
texture = new DownloadedTexture(url, imageParser);
}
this.downloadedTextures.put(url, texture);
if (texture.textureId < 0 && texture.image != null) {
texture.textureId = this.allocateAndSetupTexture(texture.image);
}
if (texture.textureId > 0) {
this.bindTexture(texture.textureId);
return true;
} else {
return false;
}
}
public boolean loadDownloadableTexture(String url, String localTexture, ImageParser imageParser) {
if (url == null) {
if (localTexture != null) {
this.bindTexture(this.getTexture(localTexture));
return true;
} else {
return false;
}
} else {
DownloadedTexture texture = (DownloadedTexture)this.downloadedTextures.get(url);
if (texture == null) {
texture = new DownloadedTexture(null, imageParser);
if( url.startsWith("offlineSkinLocal") ) {
if( OfflineSkinMod.skins.containsKey(url.substring(17)) ) {
texture.image = OfflineSkinMod.skins.get(url.substring(17)).getSkin();
} else {
texture.image = OfflineSkinMod.skinImage;
}
} else if( url.startsWith("offlineCapeLocal") ) {
if( OfflineSkinMod.skins.containsKey(url.substring(17))) {
texture.image = OfflineSkinMod.skins.get(url.substring(17)).getCape();
} else {
texture.image = OfflineSkinMod.capeImage;
}
} else {
texture = new DownloadedTexture(url, imageParser);
}
this.downloadedTextures.put(url, texture);
}
if (texture.textureId < 0 && texture.image != null) {
texture.textureId = this.allocateAndSetupTexture(texture.image);
}
if (texture.textureId > 0) {
this.bindTexture(texture.textureId);
return true;
} else if (localTexture != null) {
this.bindTexture(this.getTexture(localTexture));
return true;
} else {
return false;
}
}
}
}

View File

@ -0,0 +1,48 @@
package alterwain.offlineskin.packet;
import alterwain.offlineskin.SkinRequestHandler;
import net.minecraft.core.net.handler.NetHandler;
import net.minecraft.core.net.packet.Packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class Packet244SkinRequest extends Packet {
private boolean requestSkin;
private boolean requestCape;
private boolean requestModelType;
public Packet244SkinRequest() {}
public Packet244SkinRequest(boolean requestSkin, boolean requestCape, boolean requestModelType) {
this.requestSkin = requestSkin;
this.requestCape = requestCape;
this.requestModelType = requestModelType;
}
@Override
public void readPacketData(DataInputStream dis) throws IOException {
this.requestSkin = dis.readBoolean();
this.requestCape = dis.readBoolean();
this.requestModelType = dis.readBoolean();
}
@Override
public void writePacketData(DataOutputStream dos) throws IOException {
dos.writeBoolean(this.requestSkin);
dos.writeBoolean(this.requestCape);
dos.writeBoolean(this.requestModelType);
}
@Override
public void processPacket(NetHandler netHandler) {
((SkinRequestHandler) netHandler).offlineSkinChanger$handleSkinRequest(this);
}
@Override
public int getPacketSize() {
return 3;
}
}

View File

@ -0,0 +1,82 @@
package alterwain.offlineskin.packet;
import alterwain.offlineskin.SkinResponseHandler;
import net.minecraft.core.net.handler.NetHandler;
import net.minecraft.core.net.packet.Packet;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class Packet245SkinResponse extends Packet {
private String username;
private byte[] skin;
private byte[] cape;
private boolean modelType;
public Packet245SkinResponse(String username, byte[] skin, byte[] cape, boolean modelType) {
this.username = username;
this.skin = skin;
this.cape = cape;
this.modelType = modelType;
}
public Packet245SkinResponse() {
this.username = "";
this.skin = new byte[0];
this.cape = new byte[0];
this.modelType = false;
}
@Override
public void readPacketData(DataInputStream dis) throws IOException {
int nameLen = dis.readInt();
byte[] ub = new byte[nameLen];
dis.read(ub);
this.username = new String(ub);
int skinLen = dis.readInt();
this.skin = new byte[skinLen];
dis.read(this.skin);
int capeLen = dis.readInt();
this.cape = new byte[capeLen];
dis.read(cape);
this.modelType = dis.readBoolean();
}
@Override
public void writePacketData(DataOutputStream dos) throws IOException {
dos.writeInt(username.length());
dos.write(username.getBytes());
dos.writeInt(skin.length);
dos.write(skin);
dos.writeInt(cape.length);
dos.write(cape);
dos.writeBoolean(modelType);
}
@Override
public void processPacket(NetHandler netHandler) {
((SkinResponseHandler) netHandler).offlineSkinChanger$handleSkinResponse(this);
}
public byte[] getSkin() {
return skin;
}
public byte[] getCape() {
return cape;
}
public boolean isModelType() {
return modelType;
}
public String getUsername() {
return username;
}
@Override
public int getPacketSize() {
return skin.length+cape.length+username.length()+13;
}
}

View File

@ -0,0 +1,78 @@
package alterwain.offlineskin.packet;
import alterwain.offlineskin.OfflineSkinMod;
import alterwain.offlineskin.SendSet;
import alterwain.offlineskin.SkinConfig;
import net.minecraft.client.Minecraft;
import net.minecraft.client.render.EntityRenderDispatcher;
import net.minecraft.client.render.entity.PlayerRenderer;
import net.minecraft.core.entity.player.EntityPlayer;
import net.minecraft.core.net.handler.NetHandler;
import net.minecraft.core.net.packet.Packet;
import java.awt.image.BufferedImage;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
public class Packet246SkinSet extends Packet {
private String username;
private byte[] skin;
private byte[] cape;
private boolean modelType;
public Packet246SkinSet(String username, byte[] skin, byte[] cape, boolean modelType) {
this.username = username;
this.skin = skin;
this.cape = cape;
this.modelType = modelType;
}
public Packet246SkinSet() {
this.username = "";
this.skin = new byte[0];
this.cape = new byte[0];
this.modelType = false;
}
@Override
public void readPacketData(DataInputStream dis) throws IOException {
int nameLen = dis.readInt();
byte[] ub = new byte[nameLen];
dis.read(ub);
this.username = new String(ub);
int skinLen = dis.readInt();
this.skin = new byte[skinLen];
dis.read(this.skin);
int capeLen = dis.readInt();
this.cape = new byte[capeLen];
dis.read(cape);
this.modelType = dis.readBoolean();
}
@Override
public void writePacketData(DataOutputStream dos) throws IOException {
dos.writeInt(username.length());
dos.write(username.getBytes());
dos.writeInt(skin.length);
dos.write(skin);
dos.writeInt(cape.length);
dos.write(cape);
dos.writeBoolean(modelType);
}
@Override
public void processPacket(NetHandler netHandler) {
BufferedImage skin1 = OfflineSkinMod.bytesToImage(this.skin);
BufferedImage cape1 = OfflineSkinMod.bytesToImage(this.cape);
OfflineSkinMod.skins.put(this.username, new SkinConfig(skin1, cape1, this.modelType));
new SendSet(this.username, false).start();
new SendSet(this.username, true).start();
}
@Override
public int getPacketSize() {
return skin.length+cape.length+username.length()+13;
}
}

View File

@ -1,38 +0,0 @@
package turniplabs.examplemod;
import net.fabricmc.api.ModInitializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import turniplabs.halplibe.helper.BlockBuilder;
import turniplabs.halplibe.util.GameStartEntrypoint;
import turniplabs.halplibe.util.RecipeEntrypoint;
public class ExampleMod implements ModInitializer, GameStartEntrypoint, RecipeEntrypoint {
public static final String MOD_ID = "examplemod";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
@Override
public void onInitialize() {
LOGGER.info("ExampleMod initialized.");
}
@Override
public void beforeGameStart() {
}
@Override
public void afterGameStart() {
}
@Override
public void onRecipesReady() {
}
@Override
public void initNamespaces() {
}
}

View File

@ -1,13 +0,0 @@
{
"required": true,
"minVersion": "0.8",
"package": "turniplabs.examplemod.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}

View File

@ -1,16 +1,16 @@
{ {
"schemaVersion": 1, "schemaVersion": 1,
"id": "examplemod", "id": "offlineskin",
"version": "${version}", "version": "${version}",
"name": "Example Mod", "name": "Offline Skins",
"description": "This mod aims to help new BTA modders.", "description": "This mod allows you to change skin/cape without purchased Minecraft.",
"authors": [ "authors": [
"Turnip Labs" "alterwain"
], ],
"contact": { "contact": {
"homepage": "", "homepage": "https://github.com/alterdekim/OfflineSkinChanger",
"sources": "" "sources": "https://github.com/alterdekim/OfflineSkinChanger"
}, },
"icon": "icon.png", "icon": "icon.png",
@ -19,20 +19,20 @@
"environment": "*", "environment": "*",
"entrypoints": { "entrypoints": {
"main": [ "main": [
"turniplabs.examplemod.ExampleMod" "alterwain.offlineskin.OfflineSkinMod"
], ],
"beforeGameStart": [ "beforeGameStart": [
"turniplabs.examplemod.ExampleMod" "alterwain.offlineskin.OfflineSkinMod"
], ],
"afterGameStart": [ "afterGameStart": [
"turniplabs.examplemod.ExampleMod" "alterwain.offlineskin.OfflineSkinMod"
], ],
"recipesReady": [ "recipesReady": [
"turniplabs.examplemod.ExampleMod" "alterwain.offlineskin.OfflineSkinMod"
] ]
}, },
"mixins": [ "mixins": [
"examplemod.mixins.json" "offlineskin.mixins.json"
], ],
"depends": { "depends": {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

After

Width:  |  Height:  |  Size: 750 B

View File

@ -0,0 +1,5 @@
gui.options.page.general.button.edit_skin=Edit player\'s skin
gui.options.page.edit_skin.button.load_skin=Load skin
gui.options.page.edit_skin.button.load_cape=Load cape
gui.options.page.edit_skin.button.close=Close
gui.options.page.edit_skin.label.title=Change skin/cape

View File

@ -0,0 +1,20 @@
{
"required": true,
"minVersion": "0.8",
"package": "alterwain.offlineskin.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
"NetClientHandlerMixin",
"NetServerHandlerMixin",
"OptionsPagesMixin",
"PacketMixin",
"PlayerManagerMixin",
"PlayerRendererMixin",
"RenderEngineMixin"
],
"client": [
],
"injectors": {
"defaultRequire": 1
}
}