Add refresh button, and prevent excessive packet spam.

Packets are sent only once every 250 ms to avoid DDOSing someone.
Accomplishes this by adding a new scheduled executor.
This commit is contained in:
Firenoo committed 2023-05-12 20:02:02 -07:00
1 parent 10781e4362
commit d7f40e33af
12 files changed
+108 -54

No files matched your search

@@ -7,6 +7,7 @@ import cpw.mods.fml.common.Mod
import cpw.mods.fml.common.SidedProxy
import cpw.mods.fml.common.event.FMLPostInitializationEvent
import cpw.mods.fml.common.event.FMLPreInitializationEvent
import cpw.mods.fml.common.event.FMLServerStoppedEvent
import cpw.mods.fml.common.registry.GameRegistry
import net.minecraftforge.common.config.Configuration
import org.apache.logging.log4j.Logger
@@ -37,4 +38,9 @@ object BetterP2P {
fun postInit(event: FMLPostInitializationEvent) {
proxy.postInit()
}
@Mod.EventHandler
fun onServerStopped(event: FMLServerStoppedEvent) {
ModNetwork.stop()
}
}
@@ -60,10 +60,10 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
private val modeButton: WidgetButton
private var type: ClientTunnelInfo? = BetterP2P.proxy.getP2PFromIndex(msg.memoryInfo.type) as? ClientTunnelInfo
private var typeCooldown: Long = System.currentTimeMillis()
private var typeUpdateReady: Boolean = true
private val typeButton: WidgetButton
private val refreshButton: WidgetButton
private val scrollBar: WidgetScrollBar
private val searchBar: MEGuiTextField
@@ -208,7 +208,7 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
(hoverText as MutableList)[0] =
I18n.format("gui.advanced_memory_card.types.filtered", "§a" + type!!.stack.displayName)
}
requestRefresh()
ModNetwork.channel.sendToServer(C2SRefreshP2PList(type?.index ?: TUNNEL_ANY))
super.func_146113_a(mc.soundHandler)
}
}
@@ -241,6 +241,14 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
}
}
}
refreshButton = object: WidgetButton(this, 0, 0, 32, 32) {
override fun mousePressed(mouseX: Int, mouseY: Int, button: Int) {
if (super.mousePressed(mc, mouseX, mouseY)) {
ModNetwork.channel.sendToServer(C2SRefreshP2PList(type?.index ?: TUNNEL_ANY))
}
}
}
}
/**
@@ -281,9 +289,12 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
buttonList.add(modeButton)
typeButton.setPosition(guiLeft - 32, guiTop + 66)
typeButton.setTexCoords(0.0, 200.0)
buttonList.add(typeButton)
refreshButton.setPosition(guiLeft - 32, guiTop + 98)
refreshButton.setTexCoords(32 * 5.0, 200.0)
buttonList.add(refreshButton)
infos.refresh()
checkInfo()
refreshOverlay()
@@ -291,26 +302,13 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
}
private fun checkInfo() {
infos.filtered.forEach { it.error = false }
// A P2P entry is considered "errored" if it is an output, and has no inputs.
infos.filtered.groupBy { it.frequency }
.filter { it.value.none { x -> !x.output } }
.forEach { it.value.forEach {
info -> info.error = true
} }
}
/**
* Request a refresh from C->S.
* TODO: Implement
*/
fun requestRefresh() {
// Let's make sure the server only gets the last request in the last 250 ms, so we don't get spammed.
val time = System.currentTimeMillis()
typeCooldown = time + 250
if (typeUpdateReady && time < typeCooldown) {
ModNetwork.channel.sendToServer(C2SRefreshP2PList(type?.index ?: TUNNEL_ANY))
typeUpdateReady = false
infos.filtered.forEach {
it.error = it.frequency != 0L && if (it.output) {
col.findInput(it.frequency) == null
} else {
col.findOutput(it.frequency) == null
}
}
}
@@ -369,10 +367,6 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
col.mouseHovered(mouseX, mouseY)
}
}
if (!typeUpdateReady && System.currentTimeMillis() > typeCooldown) {
typeUpdateReady = true
ModNetwork.channel.sendToServer(C2SRefreshP2PList(type?.index ?: TUNNEL_ANY))
}
}
/**
@@ -405,9 +399,9 @@ class GuiAdvancedMemoryCard(msg: S2CListP2P) : GuiScreen(), TextureBound {
override fun mouseClicked(mouseX: Int, mouseY: Int, mouseButton: Int) {
col.mouseClicked(mouseX, mouseY, mouseButton)
scrollBar.click(mouseX, mouseY)
modeButton.mousePressed(mouseX, mouseY, mouseButton)
resizeButton.mousePressed(mouseX, mouseY, mouseButton)
typeButton.mousePressed(mouseX, mouseY, mouseButton)
buttonList.forEach {it as WidgetButton
it.mousePressed(mouseX, mouseY, mouseButton)
}
searchBar.mouseClicked(mouseX, mouseY, mouseButton)
if (mouseButton == 1 && searchBar.isMouseIn(mouseX, mouseY)) {
this.searchBar.text = ""
@@ -97,7 +97,7 @@ class InfoList (initList: Collection<InfoWrapper>,
}
-(hits * hits) + name.length
}
else -> it.frequency + Short.MAX_VALUE
else -> it.frequency + Short.MAX_VALUE - (if (it.output) 0 else 1)
}
}
}
@@ -125,9 +125,12 @@ class WidgetP2PColumn(private val gui: GuiAdvancedMemoryCard,
}
}
private fun findInput(frequency: Long?) =
fun findInput(frequency: Long?) =
infos.filtered.find { it.frequency == frequency && !it.output }
fun findOutput(frequency: Long?) =
infos.filtered.find { it.frequency == frequency && it.output }
fun mouseClicked(mouseX: Int, mouseY: Int, mouseButton: Int) {
var clickRenameButton = false
for ((index, widget) in entries.withIndex()) {
@@ -16,7 +16,6 @@ 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 com.projecturanus.betterp2p.util.p2p.toInfo
import cpw.mods.fml.relauncher.Side
import cpw.mods.fml.relauncher.SideOnly
import net.minecraft.client.renderer.texture.IIconRegister
@@ -45,9 +44,7 @@ object ItemAdvancedMemoryCard : Item() {
private fun sendStatus(status: P2PStatus, info: MemoryInfo, player: EntityPlayerMP) {
P2PCache.statusMap[player.uniqueID] = status
ModNetwork.channel.sendTo(
S2CListP2P(status.refresh(info.type).map { it.toInfo() }, info),
player)
ModNetwork.channel.sendTo(S2CListP2P(status.refresh(info.type), info), player)
}
@SideOnly(Side.CLIENT)
@@ -5,7 +5,6 @@ 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.toInfo
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
import cpw.mods.fml.common.network.simpleimpl.MessageContext
@@ -29,7 +28,7 @@ class C2SRefreshP2PList(var type: Int = TUNNEL_ANY): IMessage {
* Client -> C2SRefreshP2P -> Server
* Handler on server side
*/
class ServerRefreshP2PListHandler : IMessageHandler<C2SRefreshP2PList, S2CListP2P?> {
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]!!
@@ -40,6 +39,7 @@ class ServerRefreshP2PListHandler : IMessageHandler<C2SRefreshP2PList, S2CListP2
} else {
listAllGridP2P(status.grid, status.player)
}
return S2CListP2P(status.refresh(status.lastP2PType).map { it.toInfo() })
ModNetwork.queueP2PListUpdate(status, ctx.serverHandler.playerEntity)
return null
}
}
@@ -5,7 +5,6 @@ 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.toInfo
import com.projecturanus.betterp2p.util.p2p.unlinkP2P
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
@@ -43,6 +42,7 @@ class ServerUnlinkP2PHandler : IMessageHandler<C2SUnlinkP2P, S2CListP2P?> {
} else {
listAllGridP2P(status.grid, status.player)
}
return S2CListP2P(status.refresh(status.lastP2PType).map { it.toInfo() })
ModNetwork.queueP2PListUpdate(status, ctx.serverHandler.playerEntity)
return null
}
}
@@ -1,12 +1,22 @@
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
object ModNetwork {
val channel: SimpleNetworkWrapper = NetworkRegistry.INSTANCE.newSimpleChannel(MODID)
private var netCache: MutableMap<UUID, NetworkState> = mutableMapOf()
private lateinit var networkWorker: ScheduledThreadPoolExecutor
fun registerNetwork() {
channel.registerMessage(ClientOpenGuiHandler::class.java, S2CListP2P::class.java, 0, Side.CLIENT)
@@ -18,5 +28,52 @@ object ModNetwork {
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)
networkWorker = ScheduledThreadPoolExecutor(1, ThreadFactory {
val th = Thread(it)
th.name = "BetterP2P-NetworkWorker"
th.isDaemon = true
th.priority = Thread.MIN_PRIORITY
th
})
}
/**
* Utility function that asks for a p2p update. Multiple requests are bundled into 1.
*/
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
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(netCache) {
state.updatePending = false
}
}, state.updateReady - System.currentTimeMillis(), TimeUnit.MILLISECONDS)
}
}
}
fun stop() {
networkWorker.shutdown()
}
}
internal data class NetworkState(
internal var updatePending: Boolean = false,
internal var updateReady: Long = System.currentTimeMillis()
)
/**
* Network cooldown time in milliseconds
*/
const val NETWORK_CD = 250L
@@ -1,7 +1,6 @@
package com.projecturanus.betterp2p.network
import com.projecturanus.betterp2p.util.p2p.P2PCache
import com.projecturanus.betterp2p.util.p2p.toInfo
import com.projecturanus.betterp2p.util.p2p.linkP2P
import cpw.mods.fml.common.network.simpleimpl.IMessage
import cpw.mods.fml.common.network.simpleimpl.IMessageHandler
@@ -15,7 +14,7 @@ class ServerLinkP2PHandler : IMessageHandler<C2SLinkP2P, IMessage?> {
if (result != null) {
status.listP2P[message.input] = result.first
status.listP2P[message.output] = result.second
return S2CRefreshInfo(status.refresh(status.lastP2PType).map { it.toInfo() })
ModNetwork.queueP2PListUpdate(status, ctx.serverHandler.playerEntity)
}
return null
}
@@ -4,11 +4,9 @@ import appeng.api.parts.IPartHost
import appeng.parts.p2p.PartP2PTunnel
import com.projecturanus.betterp2p.item.ItemAdvancedMemoryCard
import com.projecturanus.betterp2p.util.p2p.P2PCache
import com.projecturanus.betterp2p.util.p2p.toInfo
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.tileentity.TileEntity
import net.minecraft.world.World
import net.minecraftforge.common.DimensionManager
@@ -25,10 +23,7 @@ class ServerRenameP2PTunnel : IMessageHandler<C2SP2PTunnelInfo, IMessage?> {
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.channel.sendTo(
S2CListP2P(cache.refresh(cache.lastP2PType).map { it.toInfo() },
ItemAdvancedMemoryCard.getInfo(cache.player.heldItem)),
cache.player as EntityPlayerMP)
ModNetwork.queueP2PListUpdate(cache, ctx.serverHandler.playerEntity, ItemAdvancedMemoryCard.getInfo(cache.player.heldItem))
}
return null
}
@@ -4,6 +4,7 @@ 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
@@ -38,14 +39,16 @@ class P2PStatus(val grid: IGrid, val player: EntityPlayer, type: Int) {
* @param type if TUNNEL_ANY or invalid, returns the full list; else
* filters the list to the targeted type
*/
fun refresh(type: Int): List<PartP2PTunnel<*>> {
fun refresh(type: Int): List<P2PInfo> {
rebuildList()
return if (type == TUNNEL_ANY) {
listP2P.values.toList()
listP2P.values.map { it.toInfo() }
} else {
listP2P.values.filter {
(BetterP2P.proxy.getP2PFromClass(it.javaClass)?.index ?: TUNNEL_ANY) == type
}.toList()
listP2P.values.mapNotNull {
if ((BetterP2P.proxy.getP2PFromClass(it.javaClass)?.index ?: TUNNEL_ANY) == type) {
it.toInfo()
} else null
}
}
}
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB