Rewrite the entire netcode

Almost all netcode has been touched in some way or form to try to fix some
bugs with hashing. Removed all unused classes.

Still needs more code cleanup (imports, etc.) but this works.
This commit is contained in:
Firenoo committed 2023-07-23 10:19:32 -07:00
1 parent 2e0a69441c
commit a96851988e
50 files changed
+1312 -1105

No files matched your search

@@ -1,7 +1,18 @@
package com.projecturanus.betterp2p
import net.minecraft.item.ItemStack
import net.minecraft.item.crafting.ShapelessRecipes
import net.minecraft.util.IIcon
import com.glodblock.github.common.parts.PartFluidP2PInterface
import com.glodblock.github.loader.ItemAndBlockHolder
import com.projecturanus.betterp2p.client.render.RenderHandler
import com.projecturanus.betterp2p.item.ItemAdvancedMemoryCard
import com.projecturanus.betterp2p.network.ServerPlayerDisconnectHandler
import com.projecturanus.betterp2p.util.p2p.ClientTunnelInfo
import com.projecturanus.betterp2p.util.p2p.TunnelInfo
import appeng.api.AEApi
import appeng.api.config.TunnelType
import appeng.api.definitions.IItemDefinition
import appeng.parts.p2p.PartP2PGT5Power
import appeng.parts.p2p.PartP2PIC2Power
@@ -15,18 +26,8 @@ import appeng.parts.p2p.PartP2PRFPower
import appeng.parts.p2p.PartP2PRedstone
import appeng.parts.p2p.PartP2PTunnel
import appeng.parts.p2p.PartP2PTunnelME
import com.glodblock.github.common.parts.PartFluidP2PInterface
import com.glodblock.github.loader.ItemAndBlockHolder
import com.projecturanus.betterp2p.client.render.RenderHandler
import com.projecturanus.betterp2p.item.ItemAdvancedMemoryCard
import com.projecturanus.betterp2p.network.ServerPlayerDisconnectHandler
import com.projecturanus.betterp2p.util.p2p.ClientTunnelInfo
import com.projecturanus.betterp2p.util.p2p.TunnelInfo
import cpw.mods.fml.common.Loader
import cpw.mods.fml.common.registry.GameRegistry
import net.minecraft.item.ItemStack
import net.minecraft.item.crafting.ShapelessRecipes
import net.minecraft.util.IIcon
/**
* A proxy for the server
@@ -1,12 +0,0 @@
package com.projecturanus.betterp2p.capability
import com.projecturanus.betterp2p.client.gui.widget.GuiScale
import com.projecturanus.betterp2p.item.BetterMemoryCardModes
import com.projecturanus.betterp2p.network.NONE_SELECTED
const val TUNNEL_ANY: Int = -1
data class MemoryInfo(var selectedEntry: Long = NONE_SELECTED,
var frequency: Long = 0,
var mode: BetterMemoryCardModes = BetterMemoryCardModes.OUTPUT,
var gui: GuiScale = GuiScale.DYNAMIC,
var type: Int = TUNNEL_ANY)
@@ -1,10 +0,0 @@
package com.projecturanus.betterp2p.capability
data class P2PTunnelInfo(
var posX: Int = 0,
var posY: Int = 0,
var posZ: Int = 0,
var world: Int = 0,
var side: Int = 0,
var name:String? = null
)
@@ -1,36 +1,43 @@
package com.projecturanus.betterp2p.client.gui
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.FontRenderer
import net.minecraft.client.gui.GuiScreen
import net.minecraft.client.renderer.Tessellator
import net.minecraft.client.resources.I18n
import net.minecraft.util.ResourceLocation
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.MODID
import com.projecturanus.betterp2p.network.data.MemoryInfo
import com.projecturanus.betterp2p.network.data.TUNNEL_ANY
import com.projecturanus.betterp2p.client.ClientCache
import com.projecturanus.betterp2p.client.TextureBound
import com.projecturanus.betterp2p.client.gui.widget.*
import com.projecturanus.betterp2p.item.BetterMemoryCardModes
import com.projecturanus.betterp2p.item.MAX_TOOLTIP_LENGTH
import com.projecturanus.betterp2p.network.*
import com.projecturanus.betterp2p.network.data.P2PInfo
import com.projecturanus.betterp2p.network.data.P2PLocation
import com.projecturanus.betterp2p.network.packet.C2SCloseGui
import com.projecturanus.betterp2p.network.packet.C2SRefreshP2PList
import com.projecturanus.betterp2p.network.packet.C2SUpdateMemoryInfo
import com.projecturanus.betterp2p.network.packet.S2COpenGui
import com.projecturanus.betterp2p.util.p2p.ClientTunnelInfo
import appeng.client.gui.widgets.MEGuiTextField
import appeng.parts.p2p.PartP2PGT5Power
import appeng.parts.p2p.PartP2PInterface
import appeng.parts.p2p.PartP2PLiquids
import appeng.parts.p2p.PartP2PRedstone
import appeng.parts.p2p.PartP2PTunnelME
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.MODID
import com.projecturanus.betterp2p.capability.MemoryInfo
import com.projecturanus.betterp2p.capability.TUNNEL_ANY
import com.projecturanus.betterp2p.client.ClientCache
import com.projecturanus.betterp2p.client.TextureBound
import com.projecturanus.betterp2p.client.gui.widget.*
import com.projecturanus.betterp2p.item.BetterMemoryCardModes
import com.projecturanus.betterp2p.item.MAX_TOOLTIP_LENGTH
import com.projecturanus.betterp2p.network.*
import com.projecturanus.betterp2p.util.p2p.ClientTunnelInfo
import com.projecturanus.betterp2p.util.p2p.TunnelInfo
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.FontRenderer
import net.minecraft.client.gui.GuiScreen
import net.minecraft.client.renderer.Tessellator
import net.minecraft.client.resources.I18n
import net.minecraft.util.ResourceLocation
import org.lwjgl.input.Keyboard
import org.lwjgl.input.Mouse
import org.lwjgl.opengl.GL11
const val GUI_WIDTH = 288
const val GUI_TEX_HEIGHT = 264
class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
class GuiAdvancedMemoryCard(msg: S2COpenGui) : GuiScreen(), TextureBound {
private var guiLeft: Int = 0
@@ -51,7 +58,7 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
_ySize = lazy { value }
}
private var scale = msg.memoryInfo.gui
private var scale = msg.memoryInfo.guiScale
private val resizeButton: WidgetButton
private var mode = msg.memoryInfo.mode
@@ -386,14 +393,20 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
}
fun refreshInfo(infos: List<P2PInfo>) {
this.infos.rebuild(infos.map(::InfoWrapper))
this.infos.rebuild(infos.map(::InfoWrapper), scrollBar, scale.size(height - 75))
checkInfo()
refreshOverlay()
}
fun updateInfo(infos: List<P2PInfo>) {
this.infos.update(infos.map(::InfoWrapper), scrollBar, scale.size(height - 75))
checkInfo()
refreshOverlay()
}
private fun syncMemoryInfo() {
ModNetwork.channel.sendToServer(
C2SUpdateInfo(MemoryInfo(infos.selectedEntry, selectedInfo?.frequency ?: 0, mode, scale, type?.index ?: TUNNEL_ANY)))
C2SUpdateMemoryInfo(MemoryInfo(infos.selectedEntry, selectedInfo?.frequency ?: 0, mode, scale, type?.index ?: TUNNEL_ANY)))
}
override fun onGuiClosed() {
@@ -450,8 +463,8 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
* Selects the info with the hashCode, if it exists. Otherwise,
* deselect the entry.
*/
fun selectInfo(hash: Long) {
infos.select(hash)
fun selectInfo(which: P2PLocation?) {
infos.select(which)
syncMemoryInfo()
refreshOverlay()
}
@@ -462,15 +475,15 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
ClientCache.selectedFacing = null
}
else {
ClientCache.selectedPosition = arrayListOf(selectedInfo?.posX, selectedInfo?.posY, selectedInfo?.posZ)
ClientCache.selectedFacing = selectedInfo?.facing
ClientCache.selectedPosition = arrayListOf(selectedInfo?.loc?.x, selectedInfo?.loc?.y, selectedInfo?.loc?.z)
ClientCache.selectedFacing = selectedInfo?.loc?.facing
}
ClientCache.positions.clear()
ClientCache.positions.addAll(infos.sorted.filter {
it.frequency == selectedInfo?.frequency &&
it != selectedInfo &&
it.dim == mc.thePlayer.dimension
}.map { arrayListOf(it.posX, it.posY, it.posZ) to it.facing })
it.loc.dim == mc.thePlayer.dimension
}.map { arrayListOf(it.loc.x, it.loc.y, it.loc.z) to it.loc.facing })
}
override fun mouseClicked(mouseX: Int, mouseY: Int, mouseButton: Int) {
@@ -1,6 +1,7 @@
package com.projecturanus.betterp2p.client.gui
import com.projecturanus.betterp2p.network.NONE_SELECTED
import com.projecturanus.betterp2p.client.gui.widget.WidgetScrollBar
import com.projecturanus.betterp2p.network.data.P2PLocation
import kotlin.reflect.KProperty0
/**
@@ -17,7 +18,7 @@ class InfoList (initList: Collection<InfoWrapper>,
* The master map, acts as the source of truth for all items
* in this list.
*/
private val masterMap: HashMap<Long, InfoWrapper> = hashMapOf()
private val masterMap: HashMap<P2PLocation, InfoWrapper> = hashMapOf()
/**
* Sorted view of the master map. This is resorted whenever
@@ -39,20 +40,26 @@ class InfoList (initList: Collection<InfoWrapper>,
get() = search.get()
val selectedInfo: InfoWrapper?
get() = masterMap[selectedEntry]
get() {
return if (selectedEntry == null) {
null
} else {
masterMap[selectedEntry]
}
}
var selectedEntry: Long = NONE_SELECTED
var selectedEntry: P2PLocation? = null
val size: Int
get() = masterMap.size
init {
initList.forEach { masterMap[it.code] = it }
initList.forEach { masterMap[it.loc] = it }
}
fun resort() {
sorted.sortBy {
if (it.code == selectedEntry) {
if (it.loc == selectedEntry) {
-2 // Put the selected p2p at the front
// Non-Zero frequencies
} else if (it.frequency != 0L && it.frequency == selectedInfo?.frequency && !it.output) {
@@ -72,7 +79,7 @@ class InfoList (initList: Collection<InfoWrapper>,
fun refilter() {
filter.updateFilter(searchStr.lowercase())
filtered = sorted.filter {
if (it.code == selectedEntry) {
if (it.loc == selectedEntry) {
return@filter true
}
for ((f, strs) in filter.activeFilters) {
@@ -83,7 +90,7 @@ class InfoList (initList: Collection<InfoWrapper>,
true
}.sortedBy {
when {
it.code == selectedEntry -> Long.MIN_VALUE + 1
it.loc == selectedEntry -> Long.MIN_VALUE + 1
it.frequency != 0L && it.frequency == selectedInfo?.frequency && !it.output -> Long.MIN_VALUE
// Put the same frequency to the front
it.frequency != 0.toLong() && it.frequency == selectedInfo?.frequency -> Long.MIN_VALUE + 2
@@ -116,33 +123,31 @@ class InfoList (initList: Collection<InfoWrapper>,
/**
* Completely refresh the master list.
*/
fun rebuild(updateList: Collection<InfoWrapper>) {
fun rebuild(updateList: Collection<InfoWrapper>, scrollbar: WidgetScrollBar, numEntries: Int) {
masterMap.clear()
updateList.forEach { masterMap[it.code] = it }
updateList.forEach { masterMap[it.loc] = it }
sorted.clear()
sorted.addAll(masterMap.values)
resort()
// TODO: Extend the filtering mechanism.
refilter()
scrollbar.setRange(0, masterMap.size.coerceIn(0, (masterMap.size - numEntries).coerceAtLeast(0)), 23)
}
/**
* Updates the master list and sends the changes downstream to sorted/filtered.
*/
fun update(updateList: Collection<InfoWrapper>) {
updateList.forEach { masterMap[it.code] = it }
fun update(updateList: Collection<InfoWrapper>, scrollbar: WidgetScrollBar, numEntries: Int) {
updateList.forEach { masterMap[it.loc] = it }
sorted.clear()
sorted.addAll(masterMap.values)
resort()
// TODO: Extend the filtering mechanism.
refilter()
scrollbar.setRange(0, masterMap.size.coerceIn(0, (masterMap.size - numEntries).coerceAtLeast(0)), 23)
}
fun select(hashCode: Long) {
if (masterMap.containsKey(hashCode)) {
selectedEntry = hashCode
} else {
selectedEntry = NONE_SELECTED
}
fun select(which: P2PLocation?) {
selectedEntry = masterMap.getOrDefault(which, null)?.loc
}
}
@@ -1,19 +1,18 @@
package com.projecturanus.betterp2p.client.gui
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.network.P2PInfo
import com.projecturanus.betterp2p.network.hashP2P
import com.projecturanus.betterp2p.network.data.P2PInfo
import com.projecturanus.betterp2p.network.data.P2PLocation
import com.projecturanus.betterp2p.util.p2p.ClientTunnelInfo
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import net.minecraft.client.gui.GuiButton
import net.minecraft.client.resources.I18n
import net.minecraft.util.IIcon
import net.minecraftforge.common.util.ForgeDirection
@SideOnly(Side.CLIENT)
class InfoWrapper(info: P2PInfo) {
// Basic information
val code: Long by lazy {
hashP2P(posX, posY, posZ, facing.ordinal, dim)
}
var frequency: Long = info.frequency
set(value) {
if (error || value == 0L) {
@@ -25,11 +24,7 @@ class InfoWrapper(info: P2PInfo) {
}
val hasChannel = info.hasChannel
val posX: Int = info.posX
val posY: Int = info.posY
val posZ: Int = info.posZ
val dim: Int = info.world
val facing: ForgeDirection = info.facing
val loc: P2PLocation = P2PLocation(info.posX, info.posY, info.posZ, info.facing, info.dim)
val output: Boolean = info.output
val type: Int = info.type
@@ -96,7 +91,7 @@ class InfoWrapper(info: P2PInfo) {
"§bP2P - ${p2pType.dispName}",
"§e" + I18n.format("gui.advanced_memory_card.pos", info.posX, info.posY, info.posZ),
"§e" + I18n.format("gui.advanced_memory_card.side", info.facing.name),
"§e" + I18n.format("gui.advanced_memory_card.dim", info.world)
"§e" + I18n.format("gui.advanced_memory_card.dim", info.dim)
)
if (error || frequency == 0L) {
hoverInfo.add("§c" + I18n.format("gui.advanced_memory_card.p2p_status.unbound"))
@@ -107,19 +102,15 @@ class InfoWrapper(info: P2PInfo) {
}
override fun hashCode(): Int {
return code.hashCode()
return loc.hashCode()
}
override fun equals(other: Any?): Boolean {
return if (other is InfoWrapper) {
this.posX == other.posX &&
this.posY == other.posY &&
this.posZ == other.posZ &&
this.dim == other.dim &&
this.facing == other.facing
} else {
false
}
if (this === other) return true
if (this.javaClass != other?.javaClass) return false
other as InfoWrapper
return this.loc == other.loc
}
}
@@ -2,14 +2,15 @@ package com.projecturanus.betterp2p.client.gui.widget
import appeng.core.localization.GuiColors
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.capability.P2PTunnelInfo
import com.projecturanus.betterp2p.client.gui.GuiAdvancedMemoryCard
import com.projecturanus.betterp2p.client.gui.InfoList
import com.projecturanus.betterp2p.client.gui.InfoWrapper
import com.projecturanus.betterp2p.item.BetterMemoryCardModes
import com.projecturanus.betterp2p.network.*
import com.projecturanus.betterp2p.network.ModNetwork
import com.projecturanus.betterp2p.network.packet.C2SLinkP2P
import com.projecturanus.betterp2p.network.packet.C2SRenameP2P
import com.projecturanus.betterp2p.network.packet.C2SUnlinkP2P
import net.minecraft.client.gui.GuiTextField
import net.minecraftforge.common.util.ForgeDirection
import org.lwjgl.input.Keyboard
import kotlin.reflect.KProperty0
@@ -59,9 +60,10 @@ class WidgetP2PColumn(private val gui: GuiAdvancedMemoryCard,
widget.renderNameTextField = true
}
if(renameBar.info != null && renameBar.text.isNotEmpty() && renameBar.info.name != renameBar.text){
val info:InfoWrapper = renameBar.info
val info: InfoWrapper = renameBar.info
renameBar.text = renameBar.text.trim()
ModNetwork.channel.sendToServer(C2SP2PTunnelInfo(P2PTunnelInfo(info.posX,info.posY,info.posZ,info.dim, ForgeDirection.valueOf(info.facing.name).ordinal,renameBar.text)))
ModNetwork.channel.sendToServer(C2SRenameP2P(info.loc, renameBar.text))
}
renameBar.isVisible = false
renameBar.text = ""
@@ -86,26 +88,26 @@ class WidgetP2PColumn(private val gui: GuiAdvancedMemoryCard,
if (mouseButton == 1) {
gui.openTypeSelector(widget, false)
} else if (selectedInfo.get() != info) {
gui.selectInfo(info.code)
gui.selectInfo(info.loc)
}
info.bindButton.func_146113_a(gui.mc.soundHandler)
}
private fun onBindButtonClicked(info: InfoWrapper) {
if (infos.selectedEntry == NONE_SELECTED) return
if (infos.selectedEntry == null) return
when (mode()) {
BetterMemoryCardModes.INPUT -> {
BetterP2P.logger.debug("Bind ${info.code} as input")
ModNetwork.channel.sendToServer(C2SLinkP2P(info.code, infos.selectedEntry))
BetterP2P.logger.debug("Bind ${info.loc} as input")
ModNetwork.channel.sendToServer(C2SLinkP2P(info.loc, infos.selectedEntry))
}
BetterMemoryCardModes.OUTPUT -> {
BetterP2P.logger.debug("Bind ${info.code} as output")
ModNetwork.channel.sendToServer(C2SLinkP2P(infos.selectedEntry, info.code))
BetterP2P.logger.debug("Bind ${info.loc} as output")
ModNetwork.channel.sendToServer(C2SLinkP2P(infos.selectedEntry, info.loc))
}
BetterMemoryCardModes.COPY -> {
val input = findInput(infos.selectedInfo?.frequency)
if (input != null)
ModNetwork.channel.sendToServer(C2SLinkP2P(input.code, info.code))
ModNetwork.channel.sendToServer(C2SLinkP2P(input.loc, info.loc))
}
else -> {
BetterP2P.logger.debug("Somehow bind button was pressed while in UNBIND mode.")
@@ -116,7 +118,7 @@ class WidgetP2PColumn(private val gui: GuiAdvancedMemoryCard,
private fun onUnbindButtonClicked(info: InfoWrapper) {
if (info.frequency != 0L) {
ModNetwork.channel.sendToServer(C2SUnlinkP2P(info.code, gui.getTypeID()))
ModNetwork.channel.sendToServer(C2SUnlinkP2P(info.loc, gui.getTypeID()))
info.frequency = 0L
}
}
@@ -1,12 +1,11 @@
package com.projecturanus.betterp2p.client.gui.widget
import com.projecturanus.betterp2p.capability.TUNNEL_ANY
import com.projecturanus.betterp2p.network.data.TUNNEL_ANY
import com.projecturanus.betterp2p.client.gui.*
import com.projecturanus.betterp2p.item.BetterMemoryCardModes
import com.projecturanus.betterp2p.network.C2STypeChange
import com.projecturanus.betterp2p.network.packet.C2STypeChange
import com.projecturanus.betterp2p.network.ModNetwork
import com.projecturanus.betterp2p.util.p2p.ClientTunnelInfo
import com.projecturanus.betterp2p.util.p2p.TunnelInfo
import net.minecraft.client.gui.GuiScreen
import net.minecraft.client.renderer.Tessellator
import net.minecraft.client.resources.I18n
@@ -58,7 +57,7 @@ class WidgetP2PDevice(private val selectedInfoProperty: KProperty0<InfoWrapper?>
} else {
// Other modes:
// Bind allowed only if currently not selected && selected is unbound; OR not bound to selected
info.bindButton.enabled = info.code != selectedInfo!!.code &&
info.bindButton.enabled = info.loc != selectedInfo!!.loc &&
(selectedInfo!!.frequency == 0L ||
info.frequency != selectedInfo!!.frequency)
info.unbindButton.enabled = false
@@ -69,7 +68,7 @@ class WidgetP2PDevice(private val selectedInfoProperty: KProperty0<InfoWrapper?>
val info = infoSupplier()
if (info != null) {
// Draw the background first
if (selectedInfo?.code == info.code)
if (selectedInfo?.loc == info.loc)
GuiScreen.drawRect(x, y, x + P2PEntryConstants.WIDTH, y + P2PEntryConstants.HEIGHT, P2PEntryConstants.SELECTED_COLOR)
else if (info.error) {
// P2P output without an input, or unbound
@@ -138,7 +137,7 @@ class WidgetP2PDevice(private val selectedInfoProperty: KProperty0<InfoWrapper?>
}
override fun accept(type: ClientTunnelInfo?) {
ModNetwork.channel.sendToServer(C2STypeChange(type?.index ?: TUNNEL_ANY, infoSupplier()!!.code))
ModNetwork.channel.sendToServer(C2STypeChange(type?.index ?: TUNNEL_ANY, infoSupplier()!!.loc))
gui.closeTypeSelector()
}
@@ -1,27 +1,26 @@
package com.projecturanus.betterp2p.item
import appeng.api.config.SecurityPermissions
import appeng.api.networking.IGridHost
import appeng.api.networking.security.ISecurityGrid
import appeng.core.CreativeTab
import appeng.parts.p2p.PartP2PTunnel
import com.projecturanus.betterp2p.MODID
import com.projecturanus.betterp2p.capability.MemoryInfo
import com.projecturanus.betterp2p.capability.TUNNEL_ANY
import com.projecturanus.betterp2p.client.ClientCache
import com.projecturanus.betterp2p.client.gui.widget.GuiScale
import com.projecturanus.betterp2p.network.ModNetwork
import com.projecturanus.betterp2p.network.NONE_SELECTED
import com.projecturanus.betterp2p.network.S2CListP2P
import com.projecturanus.betterp2p.network.hashP2P
import com.projecturanus.betterp2p.network.data.MemoryInfo
import com.projecturanus.betterp2p.network.data.TUNNEL_ANY
import com.projecturanus.betterp2p.network.data.readP2PLocation
import com.projecturanus.betterp2p.network.data.toLoc
import com.projecturanus.betterp2p.network.data.writeP2PLocation
import com.projecturanus.betterp2p.util.getPart
import com.projecturanus.betterp2p.util.p2p.P2PCache
import com.projecturanus.betterp2p.util.p2p.P2PStatus
import com.projecturanus.betterp2p.util.p2p.getTypeIndex
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import net.minecraft.client.renderer.texture.IIconRegister
import net.minecraft.entity.Entity
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.item.Item
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
@@ -42,11 +41,6 @@ object ItemAdvancedMemoryCard : Item() {
super.onUpdate(stack, worldIn, entityIn, itemSlot, isSelected)
}
private fun sendStatus(status: P2PStatus, info: MemoryInfo, player: EntityPlayerMP) {
P2PCache.statusMap[player.uniqueID] = status
ModNetwork.channel.sendTo(S2CListP2P(status.refresh(info.type), info), player)
}
@SideOnly(Side.CLIENT)
override fun addInformation(stack: ItemStack, player: EntityPlayer, tooltip: MutableList<Any?>, boolean: Boolean) {
val info = getInfo(stack)
@@ -70,29 +64,27 @@ object ItemAdvancedMemoryCard : Item() {
val te = w.getTileEntity(x, y, z)
if (te is IGridHost && te.getGridNode(ForgeDirection.getOrientation(side)) != null) {
val part = getPart(w, x, y, z, hx, hy, hz)
val grid = part?.gridNode?.grid ?: return false
if (grid is ISecurityGrid && !grid.hasPermission(player, SecurityPermissions.BUILD)) {
// Check security grid permissions. Only BUILD is required.
return false
}
val stack = player.heldItem
val info = getInfo(stack)
val type: Int
val status: P2PStatus
if (part is PartP2PTunnel<*>) {
type = part.getTypeIndex()
status = P2PStatus(part.gridNode.grid, player, type)
info.selectedEntry = hashP2P(part)
info.selectedEntry = part.toLoc()
} else {
type = TUNNEL_ANY
status = if (part != null) {
// If we have a PartP2PTunnelME, it looks at the P2P grid node,
// so we should use the part instead.
P2PStatus(part.gridNode.grid, player, type)
} else {
// Fall back to the tile entity.
P2PStatus(te.getGridNode(ForgeDirection.getOrientation(side)).grid, player, type)
}
info.selectedEntry = NONE_SELECTED
info.selectedEntry = null
}
info.type = type
writeInfo(stack, info)
sendStatus(status, info, player as EntityPlayerMP)
ModNetwork.initConnection(player, grid, info)
return true
}
}
@@ -110,12 +102,22 @@ object ItemAdvancedMemoryCard : Item() {
fun getInfo(stack: ItemStack): MemoryInfo {
if (stack.item != this) throw ClassCastException("Cannot cast ${stack.item.javaClass.name} to ${javaClass.name}")
if (stack.tagCompound == null) stack.tagCompound = NBTTagCompound()
// Initialize NBT if it isn't already a thing
if (stack.tagCompound == null) {
stack.tagCompound = NBTTagCompound()
}
val compound = stack.tagCompound!!
if (!compound.hasKey("gui")) compound.setByte("gui", GuiScale.DYNAMIC.ordinal.toByte())
if (!compound.hasKey("selectedIndex", Constants.NBT.TAG_LONG)) compound.setLong("selectedIndex", NONE_SELECTED)
return MemoryInfo(compound.getLong("selectedIndex"), compound.getLong("frequency"), BetterMemoryCardModes.values()[compound.getInteger("mode")], GuiScale.values()[compound.getByte("gui").toInt()])
if (!compound.hasKey("gui")) {
compound.setByte("gui", GuiScale.DYNAMIC.ordinal.toByte())
}
if (!compound.hasKey("selectedIndex", Constants.NBT.TAG_COMPOUND)) {
compound.setTag("selectedIndex", NBTTagCompound())
}
return MemoryInfo(
selectedEntry = readP2PLocation(compound.getCompoundTag("selectedIndex")),
frequency = compound.getLong("frequency"),
mode = BetterMemoryCardModes.values()[compound.getInteger("mode")],
guiScale = GuiScale.values()[compound.getByte("gui").toInt()])
}
fun writeInfo(stack: ItemStack, info: MemoryInfo) {
@@ -123,9 +125,9 @@ object ItemAdvancedMemoryCard : Item() {
if (stack.tagCompound == null) stack.tagCompound = NBTTagCompound()
val compound = stack.tagCompound!!
compound.setLong("selectedIndex", info.selectedEntry)
compound.setTag("selectedIndex", writeP2PLocation(info.selectedEntry))
compound.setLong("frequency", info.frequency)
compound.setInteger("mode", info.mode.ordinal)
compound.setByte("gui", info.gui.ordinal.toByte())
compound.setByte("gui", info.guiScale.ordinal.toByte())
}
}
@@ -1,24 +0,0 @@
package com.projecturanus.betterp2p.network
import cpw.mods.fml.common.network.simpleimpl.IMessage
import io.netty.buffer.ByteBuf
class AEPartLocationMessage(var x: Int = 0, var y: Int = 0, var z: Int = 0, var hitX: Float = 0f, var hitY: Float = 0f, var hitZ: Float = 0f): IMessage {
override fun fromBytes(buf: ByteBuf) {
x = buf.readInt()
y = buf.readInt()
z = buf.readInt()
hitX = buf.readFloat()
hitY = buf.readFloat()
hitZ = buf.readFloat()
}
override fun toBytes(buf: ByteBuf) {
buf.writeInt(x)
buf.writeInt(y)
buf.writeInt(z)
buf.writeFloat(hitX)
buf.writeFloat(hitY)
buf.writeFloat(hitZ)
}
}
@@ -1,13 +0,0 @@
package com.projecturanus.betterp2p.network
import cpw.mods.fml.common.network.simpleimpl.IMessage
import io.netty.buffer.ByteBuf
class C2SCloseGui : IMessage {
override fun fromBytes(buf: ByteBuf) {
}
override fun toBytes(buf: ByteBuf) {
}
}
@@ -1,16 +0,0 @@
package com.projecturanus.betterp2p.network
import cpw.mods.fml.common.network.simpleimpl.IMessage
import io.netty.buffer.ByteBuf
class C2SLinkP2P(var input: Long = NONE_SELECTED, var output: Long = NONE_SELECTED): IMessage {
override fun fromBytes(buf: ByteBuf) {
input = buf.readLong()
output = buf.readLong()
}
override fun toBytes(buf: ByteBuf) {
buf.writeLong(input)
buf.writeLong(output)
}
}
@@ -1,36 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.capability.P2PTunnelInfo
import cpw.mods.fml.common.network.simpleimpl.IMessage
import io.netty.buffer.ByteBuf
fun readP2PTunnelInfo(buf: ByteBuf): P2PTunnelInfo {
return P2PTunnelInfo(buf.readInt(),buf.readInt(),buf.readInt(),buf.readInt(),buf.readInt(), buf.readBytes(buf.readInt()).toString(Charsets.UTF_8))
}
fun writeP2PTunnelInfo(buf: ByteBuf,info: P2PTunnelInfo) {
buf.writeInt(info.posX)
buf.writeInt(info.posY)
buf.writeInt(info.posZ)
buf.writeInt(info.world)
buf.writeInt(info.side)
if(info.name ==null) {
buf.writeInt(0)
}else{
val arr = info.name?.toByteArray(Charsets.UTF_8)
arr?.let { buf.writeInt(it.size) }
buf.writeBytes(arr)
}
}
class C2SP2PTunnelInfo(var info: P2PTunnelInfo = P2PTunnelInfo()): IMessage {
override fun fromBytes(buf: ByteBuf) {
info = readP2PTunnelInfo(buf)
}
override fun toBytes(buf: ByteBuf) {
writeP2PTunnelInfo(buf,info)
}
}
@@ -1,45 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.capability.TUNNEL_ANY
import com.projecturanus.betterp2p.util.listAllGridP2P
import com.projecturanus.betterp2p.util.listTargetGridP2P
import com.projecturanus.betterp2p.util.p2p.P2PCache
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
/**
* Send a request to the server to refresh the p2p list with the
* given type.
*/
class C2SRefreshP2PList(var type: Int = TUNNEL_ANY): IMessage {
override fun fromBytes(buf: ByteBuf) {
type = buf.readByte().toInt()
}
override fun toBytes(buf: ByteBuf) {
buf.writeByte(type)
}
}
/**
* Client -> C2SRefreshP2P -> Server
* Handler on server side
*/
class ServerRefreshP2PListHandler : IMessageHandler<C2SRefreshP2PList, IMessage?> {
override fun onMessage(message: C2SRefreshP2PList, ctx: MessageContext): S2CListP2P? {
if (!P2PCache.statusMap.containsKey(ctx.serverHandler.playerEntity.uniqueID)) return null
val status = P2PCache.statusMap[ctx.serverHandler.playerEntity.uniqueID]!!
status.lastP2PType = message.type
val tunnelType = BetterP2P.proxy.getP2PFromIndex(message.type)
if (tunnelType != null) {
listTargetGridP2P(status.grid, status.player, tunnelType.clazz)
} else {
listAllGridP2P(status.grid, status.player)
}
ModNetwork.queueP2PListUpdate(status, ctx.serverHandler.playerEntity)
return null
}
}
@@ -1,15 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.capability.P2PTunnelInfo
import cpw.mods.fml.common.network.simpleimpl.IMessage
import io.netty.buffer.ByteBuf
class C2STransportPlayer(var info: P2PTunnelInfo = P2PTunnelInfo()):IMessage{
override fun fromBytes(buf: ByteBuf) {
info = readP2PTunnelInfo(buf)
}
override fun toBytes(buf: ByteBuf) {
writeP2PTunnelInfo(buf,info)
}
}
@@ -1,36 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.capability.TUNNEL_ANY
import com.projecturanus.betterp2p.util.p2p.P2PCache
import com.projecturanus.betterp2p.util.p2p.changeAllP2Ps
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
class C2STypeChange(var newType: Int = TUNNEL_ANY, var p2p: Long = NONE_SELECTED): IMessage {
override fun fromBytes(buf: ByteBuf) {
newType = buf.readByte().toInt()
p2p = buf.readLong()
}
override fun toBytes(buf: ByteBuf) {
buf.writeByte(newType)
buf.writeLong(p2p)
}
}
class ServerTypeChangeHandler : IMessageHandler<C2STypeChange, S2CListP2P?> {
override fun onMessage(message: C2STypeChange, ctx: MessageContext): S2CListP2P? {
if (message.newType == TUNNEL_ANY) return null
val status = P2PCache.statusMap[ctx.serverHandler.playerEntity.uniqueID] ?: return null
val tunnel = status.listP2P[message.p2p] ?: return null
val type = BetterP2P.proxy.getP2PFromIndex(message.newType) ?: return null
val newTunnel = changeAllP2Ps(tunnel, type, ctx.serverHandler.playerEntity)
if (newTunnel) {
ModNetwork.queueP2PListUpdate(status, ctx.serverHandler.playerEntity)
}
return null
}
}
@@ -1,48 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.capability.TUNNEL_ANY
import com.projecturanus.betterp2p.util.listAllGridP2P
import com.projecturanus.betterp2p.util.listTargetGridP2P
import com.projecturanus.betterp2p.util.p2p.P2PCache
import com.projecturanus.betterp2p.util.p2p.unlinkP2P
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
/**
* Unlink input from outputs message (set freq to 0)
*/
class C2SUnlinkP2P(var p2pId: Long = NONE_SELECTED, var type: Int = TUNNEL_ANY): IMessage {
override fun fromBytes(buf: ByteBuf) {
p2pId = buf.readLong()
type = buf.readByte().toInt()
}
override fun toBytes(buf: ByteBuf) {
buf.writeLong(p2pId)
buf.writeByte(type)
}
}
/**
* Client -> C2SUnlinkP2P -> Server
* Handler on server side
*/
class ServerUnlinkP2PHandler : IMessageHandler<C2SUnlinkP2P, S2CListP2P?> {
override fun onMessage(message: C2SUnlinkP2P, ctx: MessageContext): S2CListP2P? {
val status = P2PCache.statusMap[ctx.serverHandler.playerEntity.uniqueID] ?: return null
unlinkP2P(ctx.serverHandler.playerEntity, message.p2pId, status)
status.lastP2PType = message.type
val tunnelType = BetterP2P.proxy.getP2PFromIndex(message.type)
if (tunnelType != null) {
listTargetGridP2P(status.grid, status.player, tunnelType.clazz)
} else {
listAllGridP2P(status.grid, status.player)
}
ModNetwork.queueP2PListUpdate(status, ctx.serverHandler.playerEntity)
return null
}
}
@@ -1,45 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.capability.MemoryInfo
import com.projecturanus.betterp2p.client.gui.widget.GuiScale
import com.projecturanus.betterp2p.item.BetterMemoryCardModes
import cpw.mods.fml.common.network.simpleimpl.IMessage
import io.netty.buffer.ByteBuf
fun writeMemoryInfo(buf: ByteBuf, info: MemoryInfo) {
buf.writeLong(info.selectedEntry)
buf.writeLong(info.frequency)
buf.writeInt(info.mode.ordinal)
buf.writeByte(info.gui.ordinal)
buf.writeByte(info.type)
}
fun readMemoryInfo(buf: ByteBuf): MemoryInfo {
val selectedEntry = buf.readLong()
val frequency = buf.readLong()
val mode = try {
BetterMemoryCardModes.values()[buf.readInt()]
} catch (e: Exception) {
BetterMemoryCardModes.OUTPUT
}
val gui = try {
GuiScale.values()[buf.readByte().toInt()]
} catch (e: ArrayIndexOutOfBoundsException) {
GuiScale.DYNAMIC
}
val type = buf.readByte().toInt()
return MemoryInfo(selectedEntry, frequency, mode, gui, type)
}
/**
* Request from client -> server
*/
class C2SUpdateInfo(var info: MemoryInfo = MemoryInfo()) : IMessage {
override fun fromBytes(buf: ByteBuf) {
info = readMemoryInfo(buf)
}
override fun toBytes(buf: ByteBuf) {
writeMemoryInfo(buf, info)
}
}
@@ -1,22 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.client.gui.GuiAdvancedMemoryCard
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import net.minecraft.client.Minecraft
class ClientOpenGuiHandler : IMessageHandler<S2CListP2P, IMessage?> {
@SideOnly(Side.CLIENT)
override fun onMessage(message: S2CListP2P, ctx: MessageContext): IMessage? {
val gui = Minecraft.getMinecraft().currentScreen
if(gui is GuiAdvancedMemoryCard){
gui.refreshInfo(message.infos)
}else {
Minecraft.getMinecraft().displayGuiScreen(GuiAdvancedMemoryCard(message))
}
return null
}
}
@@ -1,20 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.client.gui.GuiAdvancedMemoryCard
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import net.minecraft.client.Minecraft
class ClientRefreshInfoHandler : IMessageHandler<S2CRefreshInfo, IMessage?> {
@SideOnly(Side.CLIENT)
override fun onMessage(message: S2CRefreshInfo, ctx: MessageContext): IMessage? {
val gui = Minecraft.getMinecraft().currentScreen
if (gui is GuiAdvancedMemoryCard) {
gui.refreshInfo(message.infos)
}
return null
}
}
@@ -1,34 +1,71 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.MODID
import com.projecturanus.betterp2p.capability.MemoryInfo
import com.projecturanus.betterp2p.util.p2p.P2PStatus
import cpw.mods.fml.common.network.NetworkRegistry
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
import cpw.mods.fml.relauncher.Side
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
import java.util.UUID
import java.util.concurrent.ScheduledThreadPoolExecutor
import java.util.concurrent.ThreadFactory
import java.util.concurrent.TimeUnit
import java.util.Collections
import java.util.UUID
import java.util.WeakHashMap
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.entity.player.EntityPlayerMP
import com.projecturanus.betterp2p.MODID
import com.projecturanus.betterp2p.network.data.GridServerCache
import com.projecturanus.betterp2p.network.data.MemoryInfo
import com.projecturanus.betterp2p.network.packet.C2SCloseGui
import com.projecturanus.betterp2p.network.packet.C2SLinkP2P
import com.projecturanus.betterp2p.network.packet.C2SRefreshP2PList
import com.projecturanus.betterp2p.network.packet.C2SRenameP2P
import com.projecturanus.betterp2p.network.packet.C2STypeChange
import com.projecturanus.betterp2p.network.packet.C2SUnlinkP2P
import com.projecturanus.betterp2p.network.packet.C2SUpdateMemoryInfo
import com.projecturanus.betterp2p.network.packet.ClientOpenGuiHandler
import com.projecturanus.betterp2p.network.packet.ClientUpdateP2PHandler
import com.projecturanus.betterp2p.network.packet.S2COpenGui
import com.projecturanus.betterp2p.network.packet.S2CUpdateP2P
import com.projecturanus.betterp2p.network.packet.ServerCloseGuiHandler
import com.projecturanus.betterp2p.network.packet.ServerLinkP2PHandler
import com.projecturanus.betterp2p.network.packet.ServerRefreshP2PListHandler
import com.projecturanus.betterp2p.network.packet.ServerRenameP2PTunnel
import com.projecturanus.betterp2p.network.packet.ServerTypeChangeHandler
import com.projecturanus.betterp2p.network.packet.ServerUnlinkP2PHandler
import com.projecturanus.betterp2p.network.packet.ServerUpdateInfoHandler
import appeng.api.networking.IGrid
import cpw.mods.fml.common.network.NetworkRegistry
import cpw.mods.fml.common.network.simpleimpl.SimpleNetworkWrapper
import cpw.mods.fml.relauncher.Side
/**
* Network cooldown time in milliseconds
*/
const val NETWORK_CD = 250L
/**
* Mod network manager. Handles server <-> client communication.
*/
object ModNetwork {
val channel: SimpleNetworkWrapper = NetworkRegistry.INSTANCE.newSimpleChannel(MODID)
private var netCache: MutableMap<UUID, NetworkState> = mutableMapOf()
/** For client requests (changing viewed p2p) */
val playerState: MutableMap<UUID, PlayerRequest> = Collections.synchronizedMap(WeakHashMap())
/** Network thread. */
private lateinit var networkWorker: ScheduledThreadPoolExecutor
fun registerNetwork() {
channel.registerMessage(ClientOpenGuiHandler::class.java, S2CListP2P::class.java, 0, Side.CLIENT)
channel.registerMessage(ClientRefreshInfoHandler::class.java, S2CRefreshInfo::class.java, 1, Side.CLIENT)
channel.registerMessage(ServerLinkP2PHandler::class.java, C2SLinkP2P::class.java, 2, Side.SERVER)
channel.registerMessage(ServerCloseGuiHandler::class.java, C2SCloseGui::class.java, 3, Side.SERVER)
channel.registerMessage(ServerUpdateInfoHandler::class.java, C2SUpdateInfo::class.java, 4, Side.SERVER)
channel.registerMessage(ServerTransportHandler::class.java, C2STransportPlayer::class.java, 5, Side.SERVER)
channel.registerMessage(ServerRenameP2PTunnel::class.java, C2SP2PTunnelInfo::class.java, 6, Side.SERVER)
channel.registerMessage(ServerRefreshP2PListHandler::class.java, C2SRefreshP2PList::class.java, 7, Side.SERVER)
channel.registerMessage(ServerUnlinkP2PHandler::class.java, C2SUnlinkP2P::class.java, 8, Side.SERVER)
channel.registerMessage(ServerTypeChangeHandler::class.java, C2STypeChange::class.java, 9, Side.SERVER)
var id = 0
channel.registerMessage(ClientOpenGuiHandler::class.java, S2COpenGui::class.java, id++, Side.CLIENT)
channel.registerMessage(ClientUpdateP2PHandler::class.java, S2CUpdateP2P::class.java, id++, Side.CLIENT)
channel.registerMessage(ServerLinkP2PHandler::class.java, C2SLinkP2P::class.java, id++, Side.SERVER)
channel.registerMessage(ServerCloseGuiHandler::class.java, C2SCloseGui::class.java, id++, Side.SERVER)
channel.registerMessage(ServerUpdateInfoHandler::class.java, C2SUpdateMemoryInfo::class.java, id++, Side.SERVER)
channel.registerMessage(ServerRenameP2PTunnel::class.java, C2SRenameP2P::class.java, id++, Side.SERVER)
channel.registerMessage(ServerRefreshP2PListHandler::class.java, C2SRefreshP2PList::class.java, id++, Side.SERVER)
channel.registerMessage(ServerUnlinkP2PHandler::class.java, C2SUnlinkP2P::class.java, id++, Side.SERVER)
channel.registerMessage(ServerTypeChangeHandler::class.java, C2STypeChange::class.java, id, Side.SERVER)
networkWorker = ScheduledThreadPoolExecutor(1, ThreadFactory {
val th = Thread(it)
th.name = "BetterP2P-NetworkWorker"
@@ -39,42 +76,78 @@ object ModNetwork {
}
/**
* Utility function that asks for a p2p update. Multiple requests are bundled into 1.
* Utility function that asks for a full refresh of a specific p2p type.
*/
fun queueP2PListUpdate(status: P2PStatus, player: EntityPlayer, info: MemoryInfo? = null) {
synchronized(netCache) {
val state = netCache.getOrPut(player.uniqueID) { NetworkState() }
if (state.updateReady + NETWORK_CD < System.currentTimeMillis()) {
channel.sendTo(S2CListP2P(status.refresh(status.lastP2PType)), player as EntityPlayerMP)
state.updateReady = System.currentTimeMillis() + NETWORK_CD
} else if (!state.updatePending) {
state.updatePending = true
fun requestP2PList(player: EntityPlayer, type: Int) {
synchronized(playerState) {
val playerState = playerState[player.uniqueID] ?: return
val cache = playerState.gridCache
cache.type = type
if (playerState.updateReady + NETWORK_CD < System.currentTimeMillis()) {
channel.sendTo(S2CUpdateP2P(cache.retrieveP2PList(), true), player as EntityPlayerMP)
playerState.updateReady = System.currentTimeMillis() + NETWORK_CD
println("Sending now")
} else if (!playerState.updatePending) {
playerState.updatePending = true
networkWorker.schedule({
if (info == null) {
channel.sendTo(S2CListP2P(status.refresh(status.lastP2PType)), player as EntityPlayerMP)
} else {
channel.sendTo(S2CListP2P(status.refresh(status.lastP2PType), info), player as EntityPlayerMP)
synchronized(ModNetwork.playerState) {
channel.sendTo(S2CUpdateP2P(cache.retrieveP2PList(), true), player as EntityPlayerMP)
playerState.updatePending = false
}
synchronized(netCache) {
state.updatePending = false
}
}, state.updateReady - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
}, playerState.updateReady - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
}
}
}
/**
* Utility function that asks for a p2p update. Multiple requests are bundled into 1.
* If dirty, only an incremental update is sent.
*/
fun requestP2PUpdate(player: EntityPlayer) {
synchronized(playerState) {
val playerState = playerState[player.uniqueID] ?: return
val cache = playerState.gridCache
if (playerState.updateReady + NETWORK_CD < System.currentTimeMillis()) {
channel.sendTo(S2CUpdateP2P(cache.getP2PUpdates()), player as EntityPlayerMP)
playerState.updateReady = System.currentTimeMillis() + NETWORK_CD
} else if (!playerState.updatePending) {
playerState.updatePending = true
networkWorker.schedule({
synchronized(ModNetwork.playerState) {
channel.sendTo(S2CUpdateP2P(cache.getP2PUpdates()), player as EntityPlayerMP)
playerState.updatePending = false
}
}, playerState.updateReady - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
}
}
}
/**
* Sets up a connection.
*/
fun initConnection(player: EntityPlayer, grid: IGrid, info: MemoryInfo) {
val cache = GridServerCache(grid, player, info.type)
playerState[player.uniqueID] = PlayerRequest(gridCache = cache)
channel.sendTo(S2COpenGui(cache.retrieveP2PList(), info), player as EntityPlayerMP)
}
fun removeConnection(player: EntityPlayer) {
playerState.remove(player.uniqueID)
}
fun stop() {
networkWorker.shutdown()
}
}
internal data class NetworkState(
internal var updatePending: Boolean = false,
internal var updateReady: Long = System.currentTimeMillis()
)
/**
* Network cooldown time in milliseconds
* Keeps track of when to send network updates.
*/
const val NETWORK_CD = 250L
data class PlayerRequest (
internal var updatePending: Boolean = false,
internal var updateReady: Long = System.currentTimeMillis(),
val gridCache: GridServerCache,
)
@@ -1,34 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.client.gui.InfoWrapper
import net.minecraftforge.common.util.ForgeDirection
class P2PInfo(val frequency: Long,
val posX: Int,
val posY: Int,
val posZ: Int,
val world: Int,
val facing: ForgeDirection,
val name: String,
val output: Boolean,
val hasChannel: Boolean,
// # of channels if ME P2P, or -1 else
val channels: Int,
// type of p2p, corresponds to registry in proxy
val type: Int) {
override fun hashCode(): Int {
return hashP2P(posX, posY, posZ, facing.ordinal, world).hashCode()
}
override fun equals(other: Any?): Boolean {
return if (other is InfoWrapper) {
this.posX == other.posX &&
this.posY == other.posY &&
this.posZ == other.posZ &&
this.facing == other.facing
} else {
false
}
}
}
@@ -1,26 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.capability.MemoryInfo
import cpw.mods.fml.common.network.simpleimpl.IMessage
import io.netty.buffer.ByteBuf
class S2CListP2P(var infos: List<P2PInfo> = emptyList(),
var memoryInfo: MemoryInfo = MemoryInfo()) : IMessage {
override fun fromBytes(buf: ByteBuf) {
val length = buf.readInt()
val list = ArrayList<P2PInfo>(length)
for (i in 0 until length) {
list += readInfo(buf)
}
infos = list
memoryInfo = readMemoryInfo(buf)
}
override fun toBytes(buf: ByteBuf) {
buf.writeInt(infos.size)
for (info in infos) {
writeInfo(buf, info)
}
writeMemoryInfo(buf, memoryInfo)
}
}
@@ -1,59 +0,0 @@
package com.projecturanus.betterp2p.network
import cpw.mods.fml.common.network.simpleimpl.IMessage
import io.netty.buffer.ByteBuf
import net.minecraftforge.common.util.ForgeDirection
fun readInfo(buf: ByteBuf): P2PInfo {
val freq = buf.readLong()
val posX = buf.readInt()
val posY = buf.readInt()
val posZ = buf.readInt()
val world = buf.readInt()
val facing = ForgeDirection.values()[buf.readInt()]
val nameLength = buf.readShort() - 1
var name = ""
for (i in 0..nameLength) {
name += buf.readChar()
}
val output = buf.readBoolean()
val hasChannel = buf.readBoolean()
val channels = buf.readByte().toInt()
val type = buf.readByte().toInt()
return P2PInfo(freq, posX, posY, posZ,world, facing, name, output, hasChannel, channels, type)
}
fun writeInfo(buf: ByteBuf, info: P2PInfo) {
buf.writeLong(info.frequency)
buf.writeInt(info.posX)
buf.writeInt(info.posY)
buf.writeInt(info.posZ)
buf.writeInt(info.world)
buf.writeInt(info.facing.ordinal)
buf.writeShort(info.name.length)
for (c in info.name) {
buf.writeChar(c.code)
}
buf.writeBoolean(info.output)
buf.writeBoolean(info.hasChannel)
buf.writeByte(info.channels)
buf.writeByte(info.type)
}
class S2CRefreshInfo(var infos: List<P2PInfo> = emptyList()) : IMessage {
override fun fromBytes(buf: ByteBuf) {
val length = buf.readInt()
val list = ArrayList<P2PInfo>(length)
for (i in 0 until length) {
list += readInfo(buf)
}
infos = list
}
override fun toBytes(buf: ByteBuf) {
buf.writeInt(infos.size)
for (info in infos) {
writeInfo(buf, info)
}
}
}
@@ -1,21 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.util.p2p.P2PCache
import com.projecturanus.betterp2p.util.p2p.linkP2P
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
class ServerLinkP2PHandler : IMessageHandler<C2SLinkP2P, IMessage?> {
override fun onMessage(message: C2SLinkP2P, ctx: MessageContext): IMessage? {
val status = P2PCache.statusMap[ctx.serverHandler.playerEntity.uniqueID] ?: return null
val result = linkP2P(ctx.serverHandler.playerEntity, message.input, message.output, status)
if (result != null) {
status.listP2P[message.input] = result.first
status.listP2P[message.output] = result.second
ModNetwork.queueP2PListUpdate(status, ctx.serverHandler.playerEntity)
}
return null
}
}
@@ -1,20 +1,21 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.util.p2p.P2PCache
import net.minecraftforge.common.MinecraftForge
import cpw.mods.fml.common.FMLCommonHandler
import cpw.mods.fml.common.eventhandler.SubscribeEvent
import cpw.mods.fml.common.gameevent.PlayerEvent
import net.minecraftforge.common.MinecraftForge
object ServerPlayerDisconnectHandler {
@SubscribeEvent
fun onLoggedOut(event: PlayerEvent.PlayerLoggedOutEvent) {
P2PCache.statusMap.remove(event.player.uniqueID)
ModNetwork.removeConnection(event.player)
}
fun register() {
val handler = ServerPlayerDisconnectHandler
MinecraftForge.EVENT_BUS.register(handler)
FMLCommonHandler.instance().bus().register(handler)
}
@@ -1,30 +0,0 @@
package com.projecturanus.betterp2p.network
import appeng.api.networking.IGridHost
import appeng.api.parts.IPartHost
import appeng.parts.p2p.PartP2PTunnel
import com.projecturanus.betterp2p.item.ItemAdvancedMemoryCard
import com.projecturanus.betterp2p.util.p2p.P2PCache
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import net.minecraft.tileentity.TileEntity
import net.minecraft.world.World
import net.minecraftforge.common.DimensionManager
import net.minecraftforge.common.util.ForgeDirection
/**
* Handler for renaming p2p tunnels.
*/
class ServerRenameP2PTunnel : IMessageHandler<C2SP2PTunnelInfo, IMessage?> {
override fun onMessage(message: C2SP2PTunnelInfo, ctx: MessageContext): IMessage? {
val world: World = DimensionManager.getWorld(message.info.world)
val te: TileEntity = world.getTileEntity(message.info.posX, message.info.posY, message.info.posZ)
val cache = P2PCache.statusMap[ctx.serverHandler.playerEntity.uniqueID] ?: return null
if (te is IGridHost && te.getGridNode(ForgeDirection.getOrientation(message.info.side)) != null) {
val p = (te as IPartHost).getPart(ForgeDirection.getOrientation(message.info.side)) as PartP2PTunnel<*>
p.customName = message.info.name
ModNetwork.queueP2PListUpdate(cache, ctx.serverHandler.playerEntity, ItemAdvancedMemoryCard.getInfo(cache.player.heldItem))
}
return null
}
}
@@ -1,25 +0,0 @@
package com.projecturanus.betterp2p.network
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import net.minecraft.entity.player.EntityPlayerMP
import net.minecraft.server.MinecraftServer
import net.minecraft.util.ChatComponentText
class ServerTransportHandler : IMessageHandler<C2STransportPlayer, IMessage?> {
override fun onMessage(message: C2STransportPlayer, ctx: MessageContext): IMessage? {
val player:EntityPlayerMP = ctx.serverHandler.playerEntity
val op = MinecraftServer.getServer().configurationManager.func_152596_g(player.gameProfile)
if(!op){
player.addChatMessage(ChatComponentText("You are not OP"))
}else{
if(player.worldObj.provider.dimensionId!= message.info.world)
player.travelToDimension(message.info.world)
player.setPositionAndUpdate(message.info.posX.toDouble(), message.info.posY.toDouble(), message.info.posZ.toDouble())
}
return null
}
}
@@ -1,17 +0,0 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.item.ItemAdvancedMemoryCard
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
class ServerUpdateInfoHandler : IMessageHandler<C2SUpdateInfo, IMessage?> {
override fun onMessage(message: C2SUpdateInfo, ctx: MessageContext): IMessage? {
val player = ctx.serverHandler.playerEntity
val stack = player.heldItem
if (stack.item is ItemAdvancedMemoryCard) {
ItemAdvancedMemoryCard.writeInfo(stack, message.info)
}
return null
}
}
@@ -0,0 +1,354 @@
package com.projecturanus.betterp2p.network.data
import java.lang.IllegalStateException
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.ChatComponentTranslation
import net.minecraftforge.common.util.ForgeDirection
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.util.p2p.TunnelInfo
import com.projecturanus.betterp2p.util.p2p.outputProperty
import com.projecturanus.betterp2p.util.p2p.getTypeIndex
import appeng.api.config.SecurityPermissions
import appeng.api.networking.IGrid
import appeng.api.networking.security.ISecurityGrid
import appeng.api.parts.IPart
import appeng.api.parts.PartItemStack
import appeng.helpers.IInterfaceHost
import appeng.me.GridAccessException
import appeng.me.cache.P2PCache
import appeng.parts.automation.UpgradeInventory
import appeng.parts.p2p.PartP2PTunnel
import appeng.parts.p2p.PartP2PTunnelStatic
import appeng.tile.inventory.AppEngInternalInventory
import appeng.util.Platform
/**
* When the player uses the adv memory card, this is cached on the server side
* to provide access to the Grid when the player performs actions in the GUI
* Each player has a list of p2ps that will be sent. These are tracked in [listP2P].
*/
class GridServerCache(private val grid: IGrid, val player: EntityPlayer, var type: Int) {
/**
* The P2P list. On init, this is the full list.
*/
private val listP2P: MutableMap<P2PLocation, PartP2PTunnel<*>> = mutableMapOf()
/**
* The dirty P2P list. Updates are accumulated here and sent altogether.
*/
private val dirtyP2P: MutableSet<P2PLocation> = mutableSetOf()
init {
rebuildList(type)
}
/**
* Refreshes the global p2p list
*/
private fun rebuildList(type: Int) {
synchronized(listP2P) {
listP2P.clear()
dirtyP2P.clear()
grid.machinesClasses.forEach {
// Find all P2P tunnels...
if (it.superclass.superclass == PartP2PTunnel::class.java &&
(type == TUNNEL_ANY || BetterP2P.proxy.getP2PFromIndex(type)?.clazz == it)) {
grid.getMachines(it).forEach { gridNode ->
val p2p = gridNode.machine as PartP2PTunnel<*>
listP2P[p2p.toLoc()] = p2p
}
}
}
}
}
/**
* Refreshes and gets the p2p list of the targeted type
* If [type] is [TUNNEL_ANY] or invalid, returns the full list; else filters the list to the
* targeted type
*/
fun retrieveP2PList(): List<P2PInfo> {
if (grid is ISecurityGrid && !grid.hasPermission(player, SecurityPermissions.BUILD)) {
return emptyList()
}
rebuildList(type)
return listP2P.values.mapNotNull {
val index = BetterP2P.proxy.getP2PFromClass(it.javaClass)?.index ?: TUNNEL_ANY
if (type == TUNNEL_ANY || index == type) {
it.toInfo()
} else null
}
}
/**
* Sets the entry to the p2p tunnel and marks it as dirty to be sent in the next network update.
* Only p2ps of the currently targeted type can be shown
*/
fun markDirty(key: P2PLocation, p2p: PartP2PTunnel<*>, force: Boolean = false) {
synchronized(listP2P) {
if (force || type == TUNNEL_ANY || p2p.getTypeIndex() == type) {
listP2P[key] = p2p
dirtyP2P.add(key)
}
}
}
/**
* Returns the list of P2Ps that are currently marked dirty, and clears the dirty list.
*/
fun getP2PUpdates(): List<P2PInfo> {
val result = dirtyP2P.mapNotNull {
listP2P[it]?.toInfo()
}
dirtyP2P.clear()
return result;
}
/**
* Link the two P2P tunnels together. Returns the pair of P2P tunnels on success, or null otherwise.
*/
fun linkP2P(inputIndex: P2PLocation, outputIndex: P2PLocation):
Pair<PartP2PTunnel<*>, PartP2PTunnel<*>>? {
// If these calls mess up we have bigger problems...
val input = listP2P[inputIndex] ?: return null
var output = listP2P[outputIndex] ?: return null
// Double check security permissions. In theory, the cache won't be set up in the first place
// for intruders.
if (grid is ISecurityGrid && !grid.hasPermission(player, SecurityPermissions.BUILD)) {
return null
}
//change type if necessary
if (input.javaClass != output.javaClass) {
output = changeP2PType(output, BetterP2P.proxy.getP2PFromClass(input.javaClass)!!) ?: return null
}
// Network loop
if (input == output) {
return null
}
var frequency = input.frequency
val cache = input.proxy.p2P
// Generate a new frequency if needed
if (input.frequency == 0L || input.isOutput) {
frequency = System.currentTimeMillis()
}
// If tunnel was already bound, unbind that one
if (cache.getInput(frequency) != null) {
val originalInput = cache.getInput(frequency)
if (originalInput != input) {
updateP2P(originalInput.toLoc(), originalInput, frequency, true, input.customName)
}
}
// Perform the link
val inputResult: PartP2PTunnel<*> = updateP2P(inputIndex, input, frequency, false, input.customName)
val outputResult: PartP2PTunnel<*> = updateP2P(outputIndex, output, frequency, true, input.customName)
// Special case for interfaces
if (input is IInterfaceHost && output is IInterfaceHost) {
val drops = mutableListOf<ItemStack>()
handleInterface(input, output, inputResult as IInterfaceHost, outputResult as IInterfaceHost, drops)
Platform.spawnDrops(player.worldObj, output.location.x, output.location.y, output.location.z, drops)
}
return inputResult to outputResult
}
fun unlinkP2P(p2pIndex: P2PLocation): PartP2PTunnel<*>? {
val tunnel = listP2P[p2pIndex] ?: return null
val oldFreq = tunnel.frequency
if (oldFreq == 0L) {
return tunnel
}
return updateP2P(p2pIndex, tunnel, 0L, false, tunnel.customName)
}
/**
* Sets the p2p tunnel to the frequency, output, and custom name. Removes the old one and replaces it, which lets
* AE2 trigger the Grid refresh for us (though we need to update the tunnels ourselves)
*/
private fun updateP2P(key: P2PLocation, tunnel: PartP2PTunnel<*>, frequency: Long, output: Boolean, name: String): PartP2PTunnel<*> {
val side = tunnel.side
val data = NBTTagCompound()
tunnel.host.removePart(side, true)
val p2pItem: ItemStack = tunnel.getItemStack(PartItemStack.Wrench)
p2pItem.writeToNBT(data)
data.setLong("freq", frequency)
val newType = ItemStack.loadItemStackFromNBT(data)
val dir: ForgeDirection = tunnel.host?.addPart(newType, side, player) ?: throw RuntimeException("Cannot bind")
val newBus: IPart = tunnel.host.getPart(dir)
if (newBus is PartP2PTunnel<*>) {
newBus.outputProperty = output
if (!name.isBlank()) {
newBus.customName = name
}
try {
val p2p = newBus.proxy.p2P
p2p.updateFreq(newBus, frequency)
} catch (e: GridAccessException) {
// :P
}
newBus.onTunnelNetworkChange()
markDirty(key, newBus)
return newBus
} else {
throw IllegalStateException("Cannot bind")
}
}
/**
* Converts one P2P into the type
*/
private fun changeP2PType(tunnel: PartP2PTunnel<*>, newType: TunnelInfo): PartP2PTunnel<*>? {
if (BetterP2P.proxy.getP2PFromClass(tunnel.javaClass) == newType) {
player.addChatMessage(ChatComponentTranslation("gui.advanced_memory_card.error.same_type"))
return null
}
// Change to a static P2P type = require p2p in inventory
if (newType.clazz.superclass == PartP2PTunnelStatic::class.java) {
var canConvert = false
for ((i, stack) in player.inventory.mainInventory.withIndex()) {
if (stack?.isItemEqual(newType.stack) == true) {
player.inventory.decrStackSize(i, 1)
canConvert = true
break
}
}
if (!canConvert) {
player.addChatMessage(ChatComponentTranslation("gui.advanced_memory_card.error.missing_items", 1, newType.stack.displayName))
return null
}
}
// Change from a static P2P type = give p2p back to the player
if (tunnel is PartP2PTunnelStatic<*>) {
val drop = ItemStack.copyItemStack(tunnel.itemStack)
drop.stackSize = 1
if (!player.inventory.addItemStackToInventory(drop)) {
val drops = mutableListOf<ItemStack>(drop)
tunnel.getDrops(drops, false)
Platform.spawnDrops(player.worldObj, player.serverPosX, player.serverPosY, player.serverPosZ, drops)
}
}
val host = tunnel.host
host.removePart(tunnel.side, false)
val dir = host.addPart(newType.stack, tunnel.side, player)
val newPart = host.getPart(dir)
if (newPart is PartP2PTunnel<*>) {
newPart.outputProperty = tunnel.isOutput
try {
markDirty(tunnel.toLoc(), newPart, true)
val p2p: P2PCache = newPart.proxy.p2P
p2p.updateFreq(newPart, tunnel.frequency)
return newPart
} catch (e: GridAccessException) {
// :P
}
}
return null
}
/**
* Converts all connected P2Ps to a new type
*/
fun changeAllP2Ps(p2p: P2PLocation, newType: TunnelInfo): Boolean {
if (grid is ISecurityGrid && !grid.hasPermission(player, SecurityPermissions.BUILD)) {
return false
}
var tunnel = listP2P[p2p] ?: return false
try {
if (tunnel.isOutput && tunnel.input != null) {
tunnel = tunnel.input
}
val outputs = tunnel.outputs.toMutableList()
if (newType.clazz.superclass == PartP2PTunnelStatic::class.java) {
val amt = outputs.size + 1
var hasItems = 0
for (stack in player.inventory.mainInventory) {
if (stack?.isItemEqual(newType.stack) == true) {
hasItems += stack.stackSize
if (hasItems >= amt) {
break
}
}
}
if (hasItems < amt) {
player.addChatMessage(ChatComponentTranslation("gui.advanced_memory_card.error.missing_items", amt, newType.stack.displayName))
return false
}
}
changeP2PType(tunnel, newType)
for (o in outputs) {
changeP2PType(o, newType)
}
return true
} catch (e: GridAccessException) {
// :P
}
return false
}
/**
* Handles a p2p interface/dual interface case.
*/
private fun handleInterface(oldIn: IInterfaceHost, oldOut: IInterfaceHost,
newIn: IInterfaceHost, newOut: IInterfaceHost,
drops: MutableList<ItemStack>) {
// For input and output, retain upgrades, items, and settings.
val upgradesIn = oldIn.interfaceDuality.getInventoryByName("upgrades") as UpgradeInventory
upgradesIn.forEachIndexed { index, stack ->
(newIn.interfaceDuality.getInventoryByName("upgrades") as UpgradeInventory).setInventorySlotContents(index, stack)
}
val upgradesOut = oldOut.interfaceDuality.getInventoryByName("upgrades") as UpgradeInventory
upgradesOut.forEachIndexed { index, stack ->
(newOut.interfaceDuality.getInventoryByName("upgrades") as UpgradeInventory).setInventorySlotContents(index, stack)
}
val itemsIn = oldIn.interfaceDuality.storage as AppEngInternalInventory
itemsIn.forEachIndexed { index, stack ->
(newIn.interfaceDuality.storage as AppEngInternalInventory).setInventorySlotContents(index, stack)
}
val itemsOut = oldOut.interfaceDuality.storage as AppEngInternalInventory
itemsOut.forEachIndexed { index, stack ->
(newOut.interfaceDuality.storage as AppEngInternalInventory).setInventorySlotContents(index, stack)
}
val settingsIn = oldIn.interfaceDuality.configManager
settingsIn.settings.forEach {
newIn.configManager.putSetting(it, settingsIn.getSetting(it))
}
val settingsOut = oldOut.interfaceDuality.configManager
settingsOut.settings.forEach {
newOut.configManager.putSetting(it, settingsOut.getSetting(it))
}
// For input, just copy the patterns over
val patternsIn = oldIn.interfaceDuality.patterns as AppEngInternalInventory
patternsIn.forEachIndexed { index, stack ->
(newIn.interfaceDuality.patterns as AppEngInternalInventory).setInventorySlotContents(index, stack)
}
// For output, drop items
val patternsOut = oldOut.interfaceDuality.patterns as AppEngInternalInventory
drops.addAll(patternsOut)
}
}
@@ -1,8 +1,7 @@
package com.projecturanus.betterp2p.network
package com.projecturanus.betterp2p.network.data
import appeng.parts.p2p.PartP2PTunnel
/**
* Generates a 64-bit hash code from X, Y, Z, Facing, and Dimension info.
* bits 0-47: FashHash of (x, y, z) ->
@@ -11,11 +10,11 @@ import appeng.parts.p2p.PartP2PTunnel
* bit 63: Reserved
*/
fun hashP2P(x: Int, y: Int, z: Int, facing: Int, dim: Int): Long {
var ret = facing.toULong() shl 61
val ret = facing.toULong() shl 60
val lo: ULong = x.toULong() or (y.toULong() shl 32)
val hi: ULong = z.toULong() or (dim.toULong() shl 32)
var hash = hashLen16(lo, hi)
hash = hash xor (hash shr 60)
hash = hash xor (hash shr 59)
return (ret or hash).toLong()
}
@@ -54,8 +53,3 @@ private fun hashLen16(lo: ULong, hi: ULong): ULong {
return f
}
/**
* Using `0x80000000` to represent none is selected (aka MSB set)
*/
const val NONE_SELECTED: Long = Long.MIN_VALUE
@@ -0,0 +1,58 @@
package com.projecturanus.betterp2p.network.data
import com.projecturanus.betterp2p.client.gui.widget.GuiScale
import com.projecturanus.betterp2p.item.BetterMemoryCardModes
import io.netty.buffer.ByteBuf
/**
* Using `-1` to represent any tunnel type.
*/
const val TUNNEL_ANY: Int = -1
/**
* A data class for serializing p2p
* @param selectedEntry Points to the index of the selected p2p in the list.
* @param frequency Selected Frequency. 0 = not selected
* @param mode The mode
* @param guiScale The saved gui scale
* @param type The P2P type, indexed in TunnelInfo.kt
*/
data class MemoryInfo(
var selectedEntry: P2PLocation? = null,
var frequency: Long = 0,
var mode: BetterMemoryCardModes = BetterMemoryCardModes.OUTPUT,
var guiScale: GuiScale = GuiScale.DYNAMIC,
var type: Int = TUNNEL_ANY)
fun readMemoryInfo(buf: ByteBuf): MemoryInfo {
var selectedEntry: P2PLocation? = null
if (buf.readBoolean()) {
selectedEntry = readP2PLocation(buf)
}
val frequency = buf.readLong()
val mode = try {
BetterMemoryCardModes.values()[buf.readInt()]
} catch (e: Exception) {
BetterMemoryCardModes.OUTPUT
}
val guiScale = try {
GuiScale.values()[buf.readByte().toInt()]
} catch (e: ArrayIndexOutOfBoundsException) {
GuiScale.DYNAMIC
}
val type = buf.readByte().toInt()
return MemoryInfo(selectedEntry, frequency, mode, guiScale, type)
}
fun writeMemoryInfo(buf: ByteBuf, info: MemoryInfo) {
val hasSelected = info.selectedEntry != null
buf.writeBoolean(hasSelected)
if (hasSelected) {
writeP2PLocation(buf, info.selectedEntry!!)
}
buf.writeLong(info.frequency)
buf.writeInt(info.mode.ordinal)
buf.writeByte(info.guiScale.ordinal)
buf.writeByte(info.type)
}
@@ -0,0 +1,109 @@
package com.projecturanus.betterp2p.network.data
import net.minecraftforge.common.util.ForgeDirection
import com.projecturanus.betterp2p.util.p2p.getTypeIndex
import com.projecturanus.betterp2p.util.p2p.hasChannel
import appeng.me.GridNode
import appeng.parts.p2p.PartP2PTunnel
import io.netty.buffer.ByteBuf
/**
* Sent over the network to clients.
*/
data class P2PInfo(
val frequency: Long,
val posX: Int,
val posY: Int,
val posZ: Int,
val dim: Int,
val facing: ForgeDirection,
val name: String,
val output: Boolean,
val hasChannel: Boolean,
// # of channels if ME P2P, or -1 else
val channels: Int,
// type of p2p, corresponds to registry in proxy
val type: Int) {
override fun hashCode(): Int {
return hashP2P(posX, posY, posZ, facing.ordinal, dim).hashCode()
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as P2PInfo
if (this.posX != other.posX) return false
if (this.posY != other.posY) return false
if (this.posZ != other.posZ) return false
if (facing != other.facing) return false
return dim == other.dim
}
}
/**
* Deserialize the P2P info over network traffic.
*/
fun readP2PInfo(buf: ByteBuf): P2PInfo? {
try {
val freq = buf.readLong()
val posX = buf.readInt()
val posY = buf.readInt()
val posZ = buf.readInt()
val world = buf.readInt()
val facing = ForgeDirection.values()[buf.readInt()]
val nameLength = buf.readShort() - 1
val name: StringBuilder = StringBuilder()
for (i in 0..nameLength) {
name.append(buf.readChar())
}
val output = buf.readBoolean()
val hasChannel = buf.readBoolean()
val channels = buf.readByte().toInt()
val type = buf.readByte().toInt()
return P2PInfo(freq, posX, posY, posZ,world, facing, name.toString(), output, hasChannel, channels, type)
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
/**
* Serialize the P2P over the network
*/
fun writeP2PInfo(buf: ByteBuf, info: P2PInfo) {
buf.writeLong(info.frequency)
buf.writeInt(info.posX)
buf.writeInt(info.posY)
buf.writeInt(info.posZ)
buf.writeInt(info.dim)
buf.writeInt(info.facing.ordinal)
buf.writeShort(info.name.length)
for (c in info.name) {
buf.writeChar(c.code)
}
buf.writeBoolean(info.output)
buf.writeBoolean(info.hasChannel)
buf.writeByte(info.channels)
buf.writeByte(info.type)
}
/**
* Convert to [P2PInfo] by extracting the necessary fields
*/
fun PartP2PTunnel<*>.toInfo() = P2PInfo(
frequency,
location.x,
location.y,
location.z,
location.dimension,
side,
customName,
isOutput,
hasChannel,
(externalFacingNode as? GridNode)?.usedChannels() ?: -1,
getTypeIndex())
@@ -0,0 +1,94 @@
/*
Hi reviewer. If you see this message, you should make a comment about it and request changes to remove this ;)
*/
package com.projecturanus.betterp2p.network.data
import net.minecraft.nbt.NBTTagCompound
import net.minecraftforge.common.util.ForgeDirection
import appeng.parts.p2p.PartP2PTunnel
import io.netty.buffer.ByteBuf
/**
* A simplified version of P2PInfo that holds only location data. This distinction is useful for
* client->server interactions
*/
data class P2PLocation(
val x: Int,
val y: Int,
val z: Int,
val facing: ForgeDirection,
val dim: Int) {
override fun hashCode(): Int {
return hashP2P(x, y, z, facing.ordinal, dim).hashCode()
}
/**
* Autogenerated equals by IntelliJ
*/
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as P2PLocation
if (x != other.x) return false
if (y != other.y) return false
if (z != other.z) return false
if (facing != other.facing) return false
return dim == other.dim
}
}
fun writeP2PLocation(buf: ByteBuf, loc: P2PLocation) {
buf.writeInt(loc.x)
buf.writeInt(loc.y)
buf.writeInt(loc.z)
buf.writeByte(loc.facing.ordinal)
buf.writeInt(loc.dim)
}
fun readP2PLocation(buf: ByteBuf): P2PLocation? {
return try {
P2PLocation(
x = buf.readInt(),
y = buf.readInt(),
z = buf.readInt(),
facing = ForgeDirection.values()[buf.readByte().toInt()],
dim = buf.readInt())
} catch (e: Exception) {
e.printStackTrace()
null
}
}
fun writeP2PLocation(loc: P2PLocation?): NBTTagCompound {
val nbt = NBTTagCompound()
if (loc != null) {
nbt.setInteger("x", loc.x)
nbt.setInteger("y", loc.y)
nbt.setInteger("z", loc.z)
nbt.setByte("f", loc.facing.ordinal.toByte())
nbt.setInteger("d", loc.dim)
}
return nbt
}
fun readP2PLocation(tag: NBTTagCompound): P2PLocation? {
return try {
P2PLocation(
x = tag.getInteger("x"),
y = tag.getInteger("y"),
z = tag.getInteger("z"),
facing = ForgeDirection.values()[tag.getByte("f").toInt()],
dim = tag.getInteger("d"))
} catch (e: Exception) {
e.printStackTrace()
null
}
}
fun PartP2PTunnel<*>.toLoc() = P2PLocation(location.x, location.y, location.z, side, location.dimension)
@@ -1,13 +1,23 @@
package com.projecturanus.betterp2p.network
package com.projecturanus.betterp2p.network.packet
import com.projecturanus.betterp2p.network.ModNetwork
import com.projecturanus.betterp2p.util.p2p.P2PCache
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
class C2SCloseGui : IMessage {
override fun fromBytes(buf: ByteBuf) {
}
override fun toBytes(buf: ByteBuf) {
}
}
class ServerCloseGuiHandler : IMessageHandler<C2SCloseGui, IMessage?> {
override fun onMessage(message: C2SCloseGui, ctx: MessageContext): IMessage? {
P2PCache.statusMap.remove(ctx.serverHandler.playerEntity.uniqueID)
ModNetwork.playerState.remove(ctx.serverHandler.playerEntity.uniqueID)
return null
}
@@ -0,0 +1,43 @@
package com.projecturanus.betterp2p.network.packet
import com.projecturanus.betterp2p.network.ModNetwork
import com.projecturanus.betterp2p.network.data.P2PLocation
import com.projecturanus.betterp2p.network.data.readP2PLocation
import com.projecturanus.betterp2p.network.data.writeP2PLocation
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
/**
* Client->Server packet that links 2 P2Ps together.
*/
class C2SLinkP2P(var input: P2PLocation? = null, var output: P2PLocation? = null): IMessage {
override fun fromBytes(buf: ByteBuf) {
input = readP2PLocation(buf)
output = readP2PLocation(buf)
}
override fun toBytes(buf: ByteBuf) {
writeP2PLocation(buf, input!!)
writeP2PLocation(buf, output!!)
}
}
class ServerLinkP2PHandler : IMessageHandler<C2SLinkP2P, IMessage?> {
override fun onMessage(message: C2SLinkP2P, ctx: MessageContext): IMessage? {
// Validate message
if (message.input == null || message.output == null) {
return null
}
val state = ModNetwork.playerState[ctx.serverHandler.playerEntity.uniqueID] ?: return null
val result = state.gridCache.linkP2P(message.input!!, message.output!!)
if (result != null) {
ModNetwork.requestP2PUpdate(ctx.serverHandler.playerEntity)
}
return null
}
}
@@ -0,0 +1,35 @@
package com.projecturanus.betterp2p.network.packet
import com.projecturanus.betterp2p.network.ModNetwork
import com.projecturanus.betterp2p.network.data.TUNNEL_ANY
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
/**
* Send a request to the server to refresh the p2p list with the given type. This must be validated
* by the server side. This is a full refresh (not just an update).
*/
class C2SRefreshP2PList(var type: Int = TUNNEL_ANY): IMessage {
override fun fromBytes(buf: ByteBuf) {
type = buf.readByte().toInt()
}
override fun toBytes(buf: ByteBuf) {
buf.writeByte(type)
}
}
/**
* Server handler.
* Handler on server side
*/
class ServerRefreshP2PListHandler : IMessageHandler<C2SRefreshP2PList, IMessage?> {
override fun onMessage(message: C2SRefreshP2PList, ctx: MessageContext): IMessage? {
ModNetwork.requestP2PList(ctx.serverHandler.playerEntity, message.type)
return null
}
}
@@ -0,0 +1,79 @@
package com.projecturanus.betterp2p.network.packet
import net.minecraft.tileentity.TileEntity
import net.minecraft.world.World
import net.minecraftforge.common.DimensionManager
import com.projecturanus.betterp2p.network.ModNetwork
import com.projecturanus.betterp2p.network.data.P2PLocation
import com.projecturanus.betterp2p.network.data.readP2PLocation
import com.projecturanus.betterp2p.network.data.writeP2PLocation
import appeng.api.networking.IGridHost
import appeng.api.parts.IPartHost
import appeng.parts.p2p.PartP2PTunnel
import com.projecturanus.betterp2p.network.data.toLoc
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
/**
* Client->Server packet for rename operation
*/
class C2SRenameP2P(var p2p: P2PLocation? = null, var name: String = ""): IMessage {
override fun fromBytes(buf: ByteBuf) {
p2p = readP2PLocation(buf)
val length = buf.readInt()
val nameB = StringBuilder()
for (i in 0 until length) {
nameB.append(buf.readChar())
}
name = nameB.toString()
}
override fun toBytes(buf: ByteBuf) {
writeP2PLocation(buf, p2p!!)
buf.writeInt(name.length)
for (c in name) {
buf.writeChar(c.code)
}
}
}
/**
* Handler for renaming p2p tunnels.
*/
class ServerRenameP2PTunnel : IMessageHandler<C2SRenameP2P, IMessage?> {
override fun onMessage(message: C2SRenameP2P, ctx: MessageContext): IMessage? {
if (message.p2p == null) {
return null
}
val world: World = DimensionManager.getWorld(message.p2p!!.dim)
val te: TileEntity = world.getTileEntity(message.p2p!!.x, message.p2p!!.y, message.p2p!!.z)
val state = ModNetwork.playerState[ctx.serverHandler.playerEntity.uniqueID] ?: return null
val facing = message.p2p!!.facing
if (te is IGridHost && te.getGridNode(facing) != null) {
val partTunnel = (te as IPartHost).getPart(facing)
if (partTunnel is PartP2PTunnel<*>) {
partTunnel.customName = message.name
val input: PartP2PTunnel<*> = if (partTunnel.isOutput) {
partTunnel.input
} else {
partTunnel
}
// Mark all dirty.
input.outputs.forEach {
state.gridCache.markDirty(it.toLoc(), it)
}
state.gridCache.markDirty(input.toLoc(), input)
ModNetwork.requestP2PUpdate(ctx.serverHandler.playerEntity)
}
}
return null
}
}
@@ -0,0 +1,43 @@
package com.projecturanus.betterp2p.network.packet
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.network.ModNetwork
import com.projecturanus.betterp2p.network.data.P2PLocation
import com.projecturanus.betterp2p.network.data.TUNNEL_ANY
import com.projecturanus.betterp2p.network.data.readP2PLocation
import com.projecturanus.betterp2p.network.data.writeP2PLocation
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
class C2STypeChange(var newType: Int = TUNNEL_ANY, var p2p: P2PLocation? = null): IMessage {
override fun fromBytes(buf: ByteBuf) {
newType = buf.readByte().toInt()
p2p = readP2PLocation(buf)
}
override fun toBytes(buf: ByteBuf) {
buf.writeByte(newType)
writeP2PLocation(buf, p2p!!)
}
}
class ServerTypeChangeHandler : IMessageHandler<C2STypeChange, IMessage?> {
override fun onMessage(message: C2STypeChange, ctx: MessageContext): IMessage? {
// Validate message
if (message.p2p == null) {
return null
}
val state = ModNetwork.playerState[ctx.serverHandler.playerEntity.uniqueID] ?: return null
val type = BetterP2P.proxy.getP2PFromIndex(message.newType) ?: return null
if (state.gridCache.changeAllP2Ps(message.p2p!!, type)) {
ModNetwork.requestP2PUpdate(ctx.serverHandler.playerEntity)
}
return null
}
}
@@ -0,0 +1,47 @@
package com.projecturanus.betterp2p.network.packet
import com.projecturanus.betterp2p.network.ModNetwork
import com.projecturanus.betterp2p.network.data.P2PLocation
import com.projecturanus.betterp2p.network.data.TUNNEL_ANY
import com.projecturanus.betterp2p.network.data.readP2PLocation
import com.projecturanus.betterp2p.network.data.writeP2PLocation
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
/**
* Unlink input from outputs message (set freq to 0)
*/
class C2SUnlinkP2P(var p2p: P2PLocation? = null, var type: Int = TUNNEL_ANY): IMessage {
override fun fromBytes(buf: ByteBuf) {
p2p = readP2PLocation(buf)
type = buf.readByte().toInt()
}
override fun toBytes(buf: ByteBuf) {
// Clientside can crash if they want to send faulty packets :P
writeP2PLocation(buf, p2p!!)
buf.writeByte(type)
}
}
/**
* Client -> Server
* Handler on server side
*/
class ServerUnlinkP2PHandler : IMessageHandler<C2SUnlinkP2P, S2COpenGui?> {
override fun onMessage(message: C2SUnlinkP2P, ctx: MessageContext): S2COpenGui? {
// message validation
if (message.p2p == null) {
return null
}
val cache = ModNetwork.playerState[ctx.serverHandler.playerEntity.uniqueID] ?: return null
cache.gridCache.unlinkP2P(message.p2p!!)
ModNetwork.requestP2PUpdate(ctx.serverHandler.playerEntity)
return null
}
}
@@ -0,0 +1,39 @@
package com.projecturanus.betterp2p.network.packet
import com.projecturanus.betterp2p.item.ItemAdvancedMemoryCard
import com.projecturanus.betterp2p.network.data.MemoryInfo
import com.projecturanus.betterp2p.network.data.readMemoryInfo
import com.projecturanus.betterp2p.network.data.writeMemoryInfo
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import io.netty.buffer.ByteBuf
/**
* Client->Server: Update the advanced memory card data
*/
class C2SUpdateMemoryInfo(var info: MemoryInfo = MemoryInfo()) : IMessage {
override fun fromBytes(buf: ByteBuf) {
info = readMemoryInfo(buf)
}
override fun toBytes(buf: ByteBuf) {
writeMemoryInfo(buf, info)
}
}
class ServerUpdateInfoHandler : IMessageHandler<C2SUpdateMemoryInfo, IMessage?> {
override fun onMessage(message: C2SUpdateMemoryInfo, ctx: MessageContext): IMessage? {
val player = ctx.serverHandler.playerEntity
val stack = player.heldItem
if (stack.item is ItemAdvancedMemoryCard) {
ItemAdvancedMemoryCard.writeInfo(stack, message.info)
}
return null
}
}
@@ -0,0 +1,57 @@
package com.projecturanus.betterp2p.network.packet
import net.minecraft.client.Minecraft
import com.projecturanus.betterp2p.client.gui.GuiAdvancedMemoryCard
import com.projecturanus.betterp2p.network.data.*
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import io.netty.buffer.ByteBuf
/**
* A Server->Client packet that sends a list of P2Ps over the net.
*/
class S2COpenGui(var infos: List<P2PInfo> = emptyList(),
var memoryInfo: MemoryInfo = MemoryInfo()) : IMessage {
override fun fromBytes(buf: ByteBuf) {
val length = buf.readInt()
val list = ArrayList<P2PInfo>(length)
for (i in 0 until length) {
val info = readP2PInfo(buf)
if (info != null) {
list.add(info)
}
}
infos = list
memoryInfo = readMemoryInfo(buf)
}
override fun toBytes(buf: ByteBuf) {
buf.writeInt(infos.size)
infos.forEach { writeP2PInfo(buf, it) }
writeMemoryInfo(buf, memoryInfo)
}
}
/**
* Handler on client side for GUI open.
*/
class ClientOpenGuiHandler : IMessageHandler<S2COpenGui, IMessage?> {
@SideOnly(Side.CLIENT)
override fun onMessage(message: S2COpenGui, ctx: MessageContext): IMessage? {
val gui = Minecraft.getMinecraft().currentScreen
if (gui is GuiAdvancedMemoryCard) {
gui.refreshInfo(message.infos)
} else {
Minecraft.getMinecraft().displayGuiScreen(GuiAdvancedMemoryCard(message))
}
return null
}
}
@@ -0,0 +1,62 @@
package com.projecturanus.betterp2p.network.packet
import net.minecraft.client.Minecraft
import com.projecturanus.betterp2p.client.gui.GuiAdvancedMemoryCard
import com.projecturanus.betterp2p.network.data.P2PInfo
import com.projecturanus.betterp2p.network.data.readP2PInfo
import com.projecturanus.betterp2p.network.data.writeP2PInfo
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import io.netty.buffer.ByteBuf
/**
* Server -> Client packet for sending P2P updates. More lightweight than OpenGui.
*/
class S2CUpdateP2P(var infos: List<P2PInfo> = emptyList(), var clear: Boolean = false): IMessage {
override fun fromBytes(buf: ByteBuf) {
val length = buf.readInt()
val list = ArrayList<P2PInfo>(length)
for (i in 0 until length) {
val info = readP2PInfo(buf)
if (info != null) {
list.add(info)
}
}
infos = list
clear = buf.readBoolean()
}
override fun toBytes(buf: ByteBuf) {
buf.writeInt(infos.size)
infos.forEach { writeP2PInfo(buf, it) }
buf.writeBoolean(clear)
}
}
/**
* Handler on client side for updating P2Ps.
*/
class ClientUpdateP2PHandler : IMessageHandler<S2CUpdateP2P, IMessage?> {
@SideOnly(Side.CLIENT)
override fun onMessage(message: S2CUpdateP2P, ctx: MessageContext): IMessage? {
val gui = Minecraft.getMinecraft().currentScreen
if (gui is GuiAdvancedMemoryCard) {
if (message.clear) {
gui.refreshInfo(message.infos)
} else {
gui.updateInfo(message.infos)
}
}
return null
}
}
@@ -1,21 +1,16 @@
package com.projecturanus.betterp2p.util
import appeng.api.config.SecurityPermissions
import appeng.api.networking.IGrid
import appeng.api.networking.security.ISecurityGrid
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.EnumFacing
import net.minecraft.util.Vec3
import net.minecraft.world.IBlockAccess
import appeng.api.parts.IPart
import appeng.api.parts.IPartHost
import appeng.api.parts.SelectedPart
import appeng.parts.AEBasePart
import appeng.parts.ICableBusContainer
import appeng.parts.p2p.PartP2PTunnel
import appeng.parts.p2p.PartP2PTunnelNormal
import appeng.tile.networking.TileCableBus
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.tileentity.TileEntity
import net.minecraft.util.EnumFacing
import net.minecraft.util.Vec3
import net.minecraft.world.IBlockAccess
/**
* @see appeng.block.networking.BlockCableBus.cb
@@ -37,33 +32,6 @@ fun getPart(w: IBlockAccess, posX: Int, posY: Int, posZ: Int, hitX: Float, hitY:
return p?.part
}
/**
* Return a list of p2p in the part's target grid
* @param grid Grid
* @param player Player to check permission
* @param clazz P2P class type
* @return a list of p2p tunnel in the target grid, or an empty list
*/
fun listTargetGridP2P(grid: IGrid?, player: EntityPlayer, clazz: Class<out PartP2PTunnel<*>>): List<PartP2PTunnel<*>> {
if (grid is ISecurityGrid)
if (!grid.hasPermission(player, SecurityPermissions.BUILD))
return emptyList()
return grid?.getMachines(clazz)?.map { it.machine as PartP2PTunnel<*> }?.toList() ?: emptyList()
}
fun listAllGridP2P(grid: IGrid?, player: EntityPlayer): List<PartP2PTunnel<*>> {
if (grid is ISecurityGrid && !grid.hasPermission(player, SecurityPermissions.BUILD))
return emptyList()
val classes = grid?.machinesClasses?.filter {
// Find all P2P tunnels...
c -> c.superclass.superclass == PartP2PTunnel::class.java
} ?: emptyList()
val ret: MutableList<PartP2PTunnel<*>> = mutableListOf()
classes.forEach{ c -> ret.addAll(grid?.getMachines(c)?.map { it.machine as PartP2PTunnel<*> } ?: emptyList()) }
return ret
}
val AEBasePart.facingPosX: Int?
get() =
host?.location?.x?.and(side?.offsetX ?: EnumFacing.UP.frontOffsetX)
@@ -1,3 +0,0 @@
package com.projecturanus.betterp2p.util.p2p
class LinkException(message: String? = null) : RuntimeException(message)
@@ -1,10 +0,0 @@
package com.projecturanus.betterp2p.util.p2p
import java.util.*
/**
* Cache P2P status for client to use
*/
object P2PCache {
val statusMap: MutableMap<UUID, P2PStatus> = Collections.synchronizedMap(WeakHashMap())
}
@@ -1,53 +0,0 @@
package com.projecturanus.betterp2p.util.p2p
import appeng.api.networking.IGrid
import appeng.parts.p2p.PartP2PTunnel
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.capability.TUNNEL_ANY
import com.projecturanus.betterp2p.network.P2PInfo
import com.projecturanus.betterp2p.network.hashP2P
import com.projecturanus.betterp2p.util.listAllGridP2P
import net.minecraft.entity.player.EntityPlayer
/**
* When the player uses the adv memory card, this is cached on the server side
* to provide access to the Grid when the player performs actions in the GUI.
*/
class P2PStatus(val grid: IGrid, val player: EntityPlayer, type: Int) {
/**
* The P2P list. This is a list of all P2Ps, and only the requested
* ones are sent.
*/
val listP2P: MutableMap<Long, PartP2PTunnel<*>> = mutableMapOf()
/**
* Server copy of the p2p currently selected in client
*/
var lastP2PType: Int = type
/**
* Refreshes the p2p list
*/
private fun rebuildList() {
listP2P.clear()
listAllGridP2P(grid, player).forEach { listP2P[hashP2P(it)] = it }
}
/**
* Refreshes and gets the p2p list of the targeted type
* @param type if TUNNEL_ANY or invalid, returns the full list; else
* filters the list to the targeted type
*/
fun refresh(type: Int): List<P2PInfo> {
rebuildList()
return if (type == TUNNEL_ANY) {
listP2P.values.map { it.toInfo() }
} else {
listP2P.values.mapNotNull {
if ((BetterP2P.proxy.getP2PFromClass(it.javaClass)?.index ?: TUNNEL_ANY) == type) {
it.toInfo()
} else null
}
}
}
}
@@ -1,258 +1,9 @@
package com.projecturanus.betterp2p.util.p2p
import appeng.api.config.SecurityPermissions
import appeng.api.networking.IGrid
import appeng.api.networking.security.ISecurityGrid
import appeng.api.parts.IPart
import appeng.api.parts.PartItemStack
import appeng.helpers.IInterfaceHost
import appeng.me.GridAccessException
import appeng.me.GridNode
import appeng.me.cache.P2PCache
import appeng.parts.automation.UpgradeInventory
import appeng.parts.p2p.PartP2PInterface
import appeng.parts.p2p.PartP2PTunnel
import appeng.parts.p2p.PartP2PTunnelNormal
import appeng.parts.p2p.PartP2PTunnelStatic
import appeng.tile.inventory.AppEngInternalInventory
import appeng.util.Platform
import com.projecturanus.betterp2p.BetterP2P
import com.projecturanus.betterp2p.capability.TUNNEL_ANY
import com.projecturanus.betterp2p.network.P2PInfo
import net.minecraft.entity.player.EntityPlayer
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.util.ChatComponentText
import net.minecraft.util.ChatComponentTranslation
import net.minecraftforge.common.util.ForgeDirection
import com.projecturanus.betterp2p.network.data.TUNNEL_ANY
fun linkP2P(player: EntityPlayer, inputIndex: Long, outputIndex: Long, status: P2PStatus) : Pair<PartP2PTunnel<*>, PartP2PTunnel<*>>? {
// If these calls mess up we have bigger problems...
val input = status.listP2P[inputIndex] ?: return null
var output = status.listP2P[outputIndex] ?: return null
val grid: IGrid? = input.gridNode?.grid
if (grid is ISecurityGrid) {
if (!grid.hasPermission(player, SecurityPermissions.BUILD) || !grid.hasPermission(player, SecurityPermissions.SECURITY)) {
return null
}
}
// TODO Change to exception
if (input.javaClass != output.javaClass) {
//change output to input
output = changeP2P(output, BetterP2P.proxy.getP2PFromClass(input.javaClass)!!, player) ?: return null
}
if (input == output) {
// Network loop
return null
}
var frequency = input.frequency
val cache = input.proxy.p2P
// TODO reduce changes
if (input.frequency == 0L || input.isOutput) {
frequency = System.currentTimeMillis()
}
// If tunnel was already bound, unbind that one
if (cache.getInput(frequency) != null) {
val originalInput = cache.getInput(frequency)
if (originalInput != input) {
updateP2P(originalInput, frequency, true, player, input.customName)
}
}
val inputResult: PartP2PTunnel<*> = updateP2P(input, frequency, false, player, input.customName)
val outputResult: PartP2PTunnel<*> = updateP2P(output, frequency, true, player, input.customName)
if (input is IInterfaceHost && output is IInterfaceHost) {
// For input and output, retain upgrades, items, and settings.
inputResult as IInterfaceHost
outputResult as IInterfaceHost
val upgradesIn = input.interfaceDuality.getInventoryByName("upgrades") as UpgradeInventory
upgradesIn.forEachIndexed { index, stack ->
(inputResult.interfaceDuality.getInventoryByName("upgrades") as UpgradeInventory).setInventorySlotContents(index, stack)
}
val upgradesOut = output.interfaceDuality.getInventoryByName("upgrades") as UpgradeInventory
upgradesOut.forEachIndexed { index, stack ->
(outputResult.interfaceDuality.getInventoryByName("upgrades") as UpgradeInventory).setInventorySlotContents(index, stack)
}
val itemsIn = input.interfaceDuality.storage as AppEngInternalInventory
itemsIn.forEachIndexed { index, stack ->
(inputResult.interfaceDuality.storage as AppEngInternalInventory).setInventorySlotContents(index, stack)
}
val itemsOut = output.interfaceDuality.storage as AppEngInternalInventory
itemsOut.forEachIndexed { index, stack ->
(outputResult.interfaceDuality.storage as AppEngInternalInventory).setInventorySlotContents(index, stack)
}
val settingsIn = input.interfaceDuality.configManager
settingsIn.settings.forEach {
inputResult.configManager.putSetting(it, settingsIn.getSetting(it))
}
val settingsOut = output.interfaceDuality.configManager
settingsOut.settings.forEach {
outputResult.configManager.putSetting(it, settingsOut.getSetting(it))
}
// For input, just copy the patterns over
val patternsIn = input.interfaceDuality.patterns as AppEngInternalInventory
patternsIn.forEachIndexed { index, stack ->
(inputResult.interfaceDuality.patterns as AppEngInternalInventory).setInventorySlotContents(index, stack)
}
// For output, drop items
val dropItems = mutableListOf<ItemStack>()
val patternsOut = output.interfaceDuality.patterns as AppEngInternalInventory
dropItems.addAll(patternsOut)
Platform.spawnDrops(player.worldObj, output.location.x, output.location.y, output.location.z, dropItems)
}
return inputResult to outputResult
}
fun unlinkP2P(player: EntityPlayer, p2pIndex: Long, status: P2PStatus): PartP2PTunnel<*>? {
if (status.grid is ISecurityGrid) {
if (!status.grid.hasPermission(player, SecurityPermissions.BUILD) ||
!status.grid.hasPermission(player, SecurityPermissions.SECURITY)) {
return null
}
}
val tunnel = status.listP2P[p2pIndex] ?: return null
val oldFreq = tunnel.frequency
if (oldFreq == 0L) {
return tunnel
}
updateP2P(tunnel, 0L, false, player, tunnel.customName)
return tunnel
}
/**
* Converts one P2P into the type
*/
fun changeP2P(tunnel: PartP2PTunnel<*>, newType: TunnelInfo, player: EntityPlayer): PartP2PTunnel<*>? {
val grid = tunnel.gridNode.grid
if (grid is ISecurityGrid) {
if (!grid.hasPermission(player, SecurityPermissions.BUILD) ||
!grid.hasPermission(player, SecurityPermissions.SECURITY)) {
return null
}
}
if (BetterP2P.proxy.getP2PFromClass(tunnel.javaClass) == newType) {
player.addChatMessage(ChatComponentText("P2P already this type."))
return null
}
if (newType.clazz.superclass == PartP2PTunnelStatic::class.java) {
var canConvert = false
for ((i, stack) in player.inventory.mainInventory.withIndex()) {
if (stack?.isItemEqual(newType.stack) == true) {
player.inventory.decrStackSize(i, 1)
canConvert = true
break
}
}
if (!canConvert) {
player.addChatMessage(ChatComponentTranslation("gui.advanced_memory_card.error.missing_items", 1, newType.stack.displayName))
return null
}
}
if (tunnel is PartP2PTunnelStatic<*>) {
val drop = ItemStack.copyItemStack(tunnel.itemStack)
drop.stackSize = 1
if (!player.inventory.addItemStackToInventory(drop)) {
val drops = mutableListOf<ItemStack>(drop)
tunnel.getDrops(drops, false)
Platform.spawnDrops(player.worldObj, player.serverPosX, player.serverPosY, player.serverPosZ, drops)
}
}
val host = tunnel.host
host.removePart(tunnel.side, false)
val dir = host.addPart(newType.stack, tunnel.side, player)
val newPart = host.getPart(dir)
if (newPart is PartP2PTunnel<*>) {
newPart.outputProperty = tunnel.isOutput
try {
val p2p: P2PCache = newPart.proxy.p2P
p2p.updateFreq(newPart, tunnel.frequency)
return newPart
} catch (e: GridAccessException) {
// :P
}
}
return null
}
/**
* Converts all connected P2Ps to a new type
*/
fun changeAllP2Ps(tunnel: PartP2PTunnel<*>, newType: TunnelInfo, player: EntityPlayer): Boolean {
val grid = tunnel.gridNode.grid
if (grid is ISecurityGrid) {
if (!grid.hasPermission(player, SecurityPermissions.BUILD) ||
!grid.hasPermission(player, SecurityPermissions.SECURITY)) {
return false
}
}
try {
if (tunnel.isOutput && tunnel.input != null) {
return changeAllP2Ps(tunnel.input, newType, player)
} else {
val outputs = tunnel.outputs.toMutableList()
if (newType.clazz.superclass == PartP2PTunnelStatic::class.java) {
val amt = outputs.size + 1
var hasItems = false
for (stack in player.inventory.mainInventory) {
if (stack?.isItemEqual(newType.stack) == true && stack.stackSize >= amt) {
hasItems = true
break
}
}
if (!hasItems) {
player.addChatMessage(ChatComponentTranslation("gui.advanced_memory_card.error.missing_items", amt, newType.stack.displayName))
return false
}
}
changeP2P(tunnel, newType, player)
for (o in outputs) {
changeP2P(o, newType, player)
}
return true
}
} catch (e: GridAccessException) {
// :P
}
return false
}
/**
* Due to Applied Energistics' limit
*/
fun updateP2P(tunnel: PartP2PTunnel<*>, frequency: Long, output: Boolean, player: EntityPlayer, name: String): PartP2PTunnel<*> {
val side = tunnel.side
val data = NBTTagCompound()
tunnel.host.removePart(side, true)
val p2pItem: ItemStack = tunnel.getItemStack(PartItemStack.Wrench)
tunnel.outputProperty = output
tunnel.customName = name
p2pItem.writeToNBT(data)
data.setLong("freq", frequency)
val newType = ItemStack.loadItemStackFromNBT(data)
val dir: ForgeDirection = tunnel.host?.addPart(newType, side, player) ?: throw RuntimeException("Cannot bind")
val newBus: IPart = tunnel.host.getPart(dir)
if (newBus is PartP2PTunnel<*>) {
newBus.outputProperty = output
try {
val p2p = newBus.proxy.p2P
p2p.updateFreq(newBus, frequency)
} catch (e: GridAccessException) {
// :P
}
newBus.onTunnelNetworkChange()
return newBus
} else {
throw RuntimeException("Cannot bind")
}
}
import appeng.parts.p2p.PartP2PTunnel
var PartP2PTunnel<*>.outputProperty
get() = isOutput
@@ -265,11 +16,6 @@ var PartP2PTunnel<*>.outputProperty
val PartP2PTunnel<*>.hasChannel
get() = isPowered && isActive
fun PartP2PTunnel<*>.toInfo()
= P2PInfo(frequency, location.x, location.y, location.z, location.dimension, side,
customName, isOutput, hasChannel, (externalFacingNode as? GridNode)?.usedChannels() ?: -1,
getTypeIndex())
/**
* Get the type index or use TUNNEL_ANY
*/
@@ -1,9 +1,10 @@
package com.projecturanus.betterp2p.util.p2p
import appeng.parts.p2p.PartP2PTunnel
import net.minecraft.item.ItemStack
import net.minecraft.util.IIcon
import appeng.parts.p2p.PartP2PTunnel
/**
* Common tunnel info to be used on server
*/
@@ -45,3 +45,4 @@ gui.advanced_memory_card.p2p_status.offline=Offline
gui.advanced_memory_card.types.filtered=Showing: %s
gui.advanced_memory_card.types.any=§aAll
gui.advanced_memory_card.error.missing_items=Cannot change P2P type without %dx %s(s).
gui.advanced_memory_card.error.same_type=P2P already this type.