wip assets manager (2)

This commit is contained in:
Bixilon 2021-12-12 14:13:50 +01:00
parent b5d67855c0
commit e929d80cae
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
42 changed files with 433 additions and 367 deletions

View File

@ -22,6 +22,7 @@ import de.bixilon.minosoft.data.language.LanguageManager.Companion.load
import de.bixilon.minosoft.data.language.MultiLanguageManager
import de.bixilon.minosoft.data.registries.DefaultRegistries
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.versions.Versions
import de.bixilon.minosoft.gui.eros.Eros
import de.bixilon.minosoft.gui.eros.XStartOnFirstThreadWarning
import de.bixilon.minosoft.gui.eros.crash.ErosCrashReport.Companion.crash
@ -69,7 +70,7 @@ object Minosoft {
taskWorker += Task(identifier = StartupTasks.LOAD_VERSIONS, priority = ThreadPool.HIGH, executor = {
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Loading versions..." }
// Versions.loadAvailableVersions(MINOSOFT_ASSETS_MANAGER.readLegacyJsonAsset(ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "mapping/versions.json")))
Versions.load()
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Versions loaded!" }
})
@ -90,7 +91,7 @@ object Minosoft {
val language = ErosProfileManager.selected.general.language
ErosProfileManager.selected.general::language.profileWatch(this, true) {
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Loading language files (${language})" }
LANGUAGE_MANAGER.translators[ProtocolDefinition.MINOSOFT_NAMESPACE] = load(it, null, ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "language/"))
LANGUAGE_MANAGER.translators[ProtocolDefinition.MINOSOFT_NAMESPACE] = load(it, null, MINOSOFT_ASSETS_MANAGER, ResourceLocation(ProtocolDefinition.MINOSOFT_NAMESPACE, "language/"))
Log.log(LogMessageType.OTHER, LogLevels.VERBOSE) { "Language files loaded!" }
}
})

View File

@ -0,0 +1,31 @@
package de.bixilon.minosoft.assets
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.assets.minecraft.JarAssetsManager
import de.bixilon.minosoft.assets.minecraft.index.IndexAssetsManager
import de.bixilon.minosoft.assets.multi.PriorityAssetsManager
import de.bixilon.minosoft.assets.properties.version.AssetsVersionProperties
import de.bixilon.minosoft.assets.properties.version.AssetsVersionProperty
import de.bixilon.minosoft.config.profile.profiles.resources.ResourcesProfile
import de.bixilon.minosoft.data.registries.versions.Version
object AssetsLoader {
fun create(profile: ResourcesProfile, version: Version, property: AssetsVersionProperty = AssetsVersionProperties[version] ?: throw IllegalAccessException("$version has no assets!")): AssetsManager {
val assetsManager = PriorityAssetsManager()
for (resourcePack in profile.assets.resourcePacks) {
assetsManager += resourcePack.type.creator(resourcePack)
}
if (!profile.assets.disableIndexAssets) {
assetsManager += IndexAssetsManager(profile, property.indexVersion, property.indexHash, profile.assets.indexAssetsTypes.toSet())
}
if (!profile.assets.disableJarAssets) {
assetsManager += JarAssetsManager(property.jarAssetsHash, property.clientJarHash, profile, version)
}
assetsManager += Minosoft.MINOSOFT_ASSETS_MANAGER
return assetsManager
}
}

View File

@ -11,11 +11,15 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.assets.file
package de.bixilon.minosoft.assets.directory
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.assets.util.FileAssetsUtil.toAssetName
import de.bixilon.minosoft.assets.util.FileUtil
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.util.CountUpAndDownLatch
import java.io.File
import java.io.FileNotFoundException
import java.io.InputStream
@ -25,18 +29,50 @@ import java.io.InputStream
class DirectoryAssetsManager(
private val basePath: String,
) : FileAssetsManager() {
) : AssetsManager {
override val namespaces: MutableSet<String> = mutableSetOf()
private var assets: MutableSet<ResourceLocation> = mutableSetOf()
private val ResourceLocation.filePath: String
get() = "$basePath/$namespace/$path"
override fun load(latch: CountUpAndDownLatch) = Unit
// ToDo: Load properties
private fun scanDirectory(directory: File) {
for (file in directory.listFiles() ?: return) {
if (file.isDirectory) {
scanDirectory(file)
continue
}
val path = file.path.removePrefix(basePath).removePrefix("/").toAssetName(false) ?: continue
assets += path
namespaces += path.namespace
}
}
override fun load(latch: CountUpAndDownLatch) {
scanDirectory(File(basePath))
}
override fun unload() {
assets.clear()
}
override fun iterator(): Iterator<Map.Entry<ResourceLocation, ByteArray>> {
TODO("Not yet implemented")
}
override fun get(path: ResourceLocation): InputStream {
if (path !in assets) {
throw FileNotFoundException("Can not find asset $path")
}
return FileUtil.readFile(path.filePath, false)
}
override fun nullGet(path: ResourceLocation): InputStream? {
if (path !in assets) {
return null
}
return FileUtil.saveReadFile(path.filePath, false)
}
}

View File

@ -1,6 +1,7 @@
package de.bixilon.minosoft.assets.file
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.assets.directory.DirectoryAssetsManager
import java.io.FileNotFoundException
import java.net.URLDecoder
import java.nio.charset.StandardCharsets

View File

@ -41,12 +41,15 @@ class JarAssetsManager(
val version: Version,
) : MinecraftAssetsManager {
override val namespaces = setOf(ProtocolDefinition.DEFAULT_NAMESPACE)
private var jarAssets: Map<String, ByteArray> = mapOf()
private var jarAssets: MutableMap<String, ByteArray> = mutableMapOf()
override fun load(latch: CountUpAndDownLatch) {
val jarAssetFile = File(FileAssetsUtil.getPath(jarAssetsHash))
if (FileAssetsUtil.verifyAsset(jarAssetsHash, jarAssetFile, profile.verify)) {
jarAssets = FileUtil.readFile(jarAssetFile).readArchive()
val jarAssets = FileUtil.readFile(jarAssetFile).readArchive()
for ((path, data) in jarAssets) {
this.jarAssets[path.removePrefix("assets/" + ProtocolDefinition.DEFAULT_NAMESPACE + "/")] = data
}
} else {
var clientJar = FileUtil.saveReadFile(File(FileAssetsUtil.getPath(clientJarHash)), false)?.readZipArchive()
if (clientJar == null) {
@ -112,7 +115,7 @@ class JarAssetsManager(
}
override fun unload() {
jarAssets = mapOf()
jarAssets = mutableMapOf()
}
override fun iterator(): Iterator<Map.Entry<ResourceLocation, ByteArray>> {

View File

@ -20,11 +20,15 @@ import de.bixilon.minosoft.assets.util.FileUtil
import de.bixilon.minosoft.assets.util.FileUtil.readJsonObject
import de.bixilon.minosoft.config.profile.profiles.resources.ResourcesProfile
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.CountUpAndDownLatch
import de.bixilon.minosoft.util.KUtil.synchronizedMapOf
import de.bixilon.minosoft.util.KUtil.toLong
import de.bixilon.minosoft.util.Util
import de.bixilon.minosoft.util.json.Jackson
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.asCompound
import de.bixilon.minosoft.util.task.pool.DefaultThreadPool
import de.bixilon.minosoft.util.task.pool.ThreadPool
@ -39,13 +43,13 @@ import java.io.InputStream
*/
class IndexAssetsManager(
private val profile: ResourcesProfile,
private val verify: Boolean,
private val assetsVersion: String,
private val indexHash: String,
private val types: Set<IndexAssetsType>,
) : MinecraftAssetsManager {
private val verify: Boolean = profile.verify
private val assets: MutableMap<ResourceLocation, AssetsProperty> = synchronizedMapOf()
override val namespaces: MutableSet<String> = mutableSetOf()
override val namespaces: Set<String> = setOf(ProtocolDefinition.DEFAULT_NAMESPACE)
private fun downloadAssetsIndex(): Map<String, Any> {
return Jackson.MAPPER.readValue(FileAssetsUtil.downloadAndGetAsset(Util.formatString(profile.source.mojangPackages,
@ -65,6 +69,7 @@ class IndexAssetsManager(
"hashPrefix" to hash.substring(0, 2),
"fullHash" to hash,
))
Log.log(LogMessageType.ASSETS, LogLevels.VERBOSE) { "Downloading asset $url" }
val downloadedHash = FileAssetsUtil.downloadAsset(url)
if (downloadedHash != hash) {
throw IOException("Verification of asset $hash failed!")
@ -73,23 +78,34 @@ class IndexAssetsManager(
override fun load(latch: CountUpAndDownLatch) {
var assets = FileUtil.saveReadFile(FileAssetsUtil.getPath(indexHash))?.readJsonObject() ?: downloadAssetsIndex()
assets["objects"].let { assets = it.asCompound() }
val tasks = CountUpAndDownLatch(0)
val assetsLatch = CountUpAndDownLatch(assets.size, parent = latch)
val tasks = CountUpAndDownLatch(0)
assets["objects"].let { assets = it.asCompound() }
for ((path, data) in assets) {
check(data is Map<*, *>)
val name = path.toAssetName() ?: continue
val name = path.toAssetName(false)
if (name == null) {
assetsLatch.dec()
continue
}
val type = when {
name.path.startsWith("lang/") -> IndexAssetsType.LANGUAGE
name.path.startsWith("sounds/") -> IndexAssetsType.SOUNDS
name.path == "sounds.json" -> IndexAssetsType.SOUNDS
name.path.startsWith("textures/") -> IndexAssetsType.TEXTURES
else -> continue
else -> {
assetsLatch.dec()
continue
}
}
if (type !in this.types) {
assetsLatch.dec()
continue
}
namespaces += name.namespace
val size = data["size"]?.toLong() ?: -1
val hash = data["hash"].toString()
if (tasks.count > DefaultThreadPool.threadCount - 1) {

View File

@ -1,8 +1,17 @@
package de.bixilon.minosoft.assets.minecraft.index
import de.bixilon.minosoft.util.KUtil
import de.bixilon.minosoft.util.enum.ValuesEnum
enum class IndexAssetsType {
LANGUAGE,
SOUNDS,
TEXTURES,
;
companion object : ValuesEnum<IndexAssetsType> {
override val VALUES: Array<IndexAssetsType> = values()
override val NAME_MAP: Map<String, IndexAssetsType> = KUtil.getEnumValues(VALUES)
}
}

View File

@ -46,9 +46,12 @@ class PriorityAssetsManager(
fun add(manager: AssetsManager) {
for (namespace in manager.namespaces) {
this.managers.getOrPut(namespace) { mutableSetOf() } += manager
this.namespaces += namespace
}
}
operator fun plusAssign(manager: AssetsManager) = add(manager)
override fun get(path: ResourceLocation): InputStream {
return nullGet(path) ?: throw FileNotFoundException("Can not find assets-manager for $path")
}

View File

@ -16,7 +16,7 @@ object AssetsVersionProperties {
}
val assetsProperties: Map<String, AssetsVersionProperty> = Minosoft.MINOSOFT_ASSETS_MANAGER[ASSETS_PROPERTIES_FILE].readJson()
for ((versionName, property) in assetsProperties) {
PROPERTIES[Versions.getVersionByName(versionName) ?: continue] = property
PROPERTIES[Versions[versionName] ?: continue] = property
}
}

View File

@ -88,6 +88,7 @@ object FileAssetsUtil {
file.parentFile.apply {
mkdirs()
if (!isDirectory) {
tempFile.delete()
throw IllegalStateException("Could not create folder $this")
}
}
@ -108,8 +109,8 @@ object FileAssetsUtil {
return saveAndGet(ByteArrayInputStream(data), compress, false)
}
fun String.toAssetName(): ResourceLocation? {
if (!startsWith("assets/")) {
fun String.toAssetName(verifyPrefix: Boolean = true): ResourceLocation? {
if (verifyPrefix && !startsWith("assets/")) {
return null
}
val split = removePrefix("assets/").split("/", limit = 2)
@ -135,26 +136,31 @@ object FileAssetsUtil {
return true
}
val digest = MessageDigest.getInstance("SHA-1")
try {
val digest = MessageDigest.getInstance("SHA-1")
var input: InputStream = FileInputStream(file)
if (compress) {
input = ZstdInputStream(input)
}
val buffer = ByteArray(ProtocolDefinition.DEFAULT_BUFFER_SIZE)
var length: Int
while (true) {
length = input.read(buffer, 0, buffer.size)
if (length < 0) {
break
var input: InputStream = FileInputStream(file)
if (compress) {
input = ZstdInputStream(input)
}
digest.update(buffer, 0, length)
}
val equals = hash != Util.byteArrayToHexString(digest.digest())
if (!equals) {
val buffer = ByteArray(ProtocolDefinition.DEFAULT_BUFFER_SIZE)
var length: Int
while (true) {
length = input.read(buffer, 0, buffer.size)
if (length < 0) {
break
}
digest.update(buffer, 0, length)
}
val equals = hash == Util.byteArrayToHexString(digest.digest())
if (!equals) {
file.delete()
}
return equals
} catch (exception: Throwable) {
file.delete()
return false
}
return equals
}
}

View File

@ -15,7 +15,9 @@ package de.bixilon.minosoft.assets.util
import com.fasterxml.jackson.module.kotlin.readValue
import com.github.luben.zstd.ZstdInputStream
import de.bixilon.mbf.MBFBinaryReader
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.KUtil.unsafeCast
import de.bixilon.minosoft.util.json.Jackson
import de.matthiasmann.twl.utils.PNGDecoder
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream
@ -127,4 +129,8 @@ object FileUtil {
return colors
}
fun InputStream.readMBFMap(): Map<Any, Any> {
return MBFBinaryReader(this).readMBF().data.unsafeCast()
}
}

View File

@ -6,6 +6,7 @@ import de.bixilon.minosoft.config.profile.profiles.account.AccountProfile
import de.bixilon.minosoft.config.profile.profiles.account.AccountProfileManager
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager.delegate
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager.mapDelegate
import de.bixilon.minosoft.data.language.LanguageManager
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.util.KUtil.fullName
import java.util.*
@ -14,7 +15,7 @@ class GeneralC {
/**
* Language to use for eros. This is also the fallback language for other profiles
*/
var language: String by delegate(Locale.getDefault()?.fullName ?: "en_US")
var language: String by delegate(Locale.getDefault()?.fullName ?: LanguageManager.FALLBACK_LANGUAGE)
@get:JsonProperty("account_profile") private var _accountProfile: String? by delegate(null)
@ -29,5 +30,4 @@ class GeneralC {
* If profile is not set or not found, the global default profile is used
*/
var profileOverrides: MutableMap<ResourceLocation, String> by mapDelegate()
}

View File

@ -53,7 +53,7 @@ class Server(
private var _forcedVersion by delegate(forcedVersion?.name)
@get:JsonIgnore
var forcedVersion by backingDelegate(getter = { Versions.getVersionByName(_forcedVersion) }, setter = { _forcedVersion = it?.name })
var forcedVersion by backingDelegate(getter = { Versions[_forcedVersion] }, setter = { _forcedVersion = it?.name })
@get:JsonInclude(JsonInclude.Include.NON_DEFAULT)
var faviconHash: String? by delegate(null) { if (it != null) check(it.length == 40) { "Not a valid sha1 hash!" } }

View File

@ -4,6 +4,7 @@ import de.bixilon.minosoft.config.profile.ProfileManager
import de.bixilon.minosoft.config.profile.profiles.Profile
import de.bixilon.minosoft.config.profile.profiles.resources.ResourcesProfileManager.delegate
import de.bixilon.minosoft.config.profile.profiles.resources.ResourcesProfileManager.latestVersion
import de.bixilon.minosoft.config.profile.profiles.resources.assets.AssetsC
import de.bixilon.minosoft.config.profile.profiles.resources.source.SourceC
import de.bixilon.minosoft.util.KUtil.unsafeCast
@ -23,6 +24,7 @@ class ResourcesProfile(
override var description by delegate(description ?: "")
val source = SourceC()
val assets = AssetsC()
/**
* If set, all downloaded assets will be checked on load.

View File

@ -0,0 +1,15 @@
package de.bixilon.minosoft.config.profile.profiles.resources.assets
import de.bixilon.minosoft.assets.minecraft.index.IndexAssetsType
import de.bixilon.minosoft.config.profile.profiles.resources.ResourcesProfileManager.delegate
import de.bixilon.minosoft.config.profile.profiles.resources.ResourcesProfileManager.listDelegate
import de.bixilon.minosoft.config.profile.profiles.resources.ResourcesProfileManager.setDelegate
import de.bixilon.minosoft.config.profile.profiles.resources.assets.packs.ResourcePack
class AssetsC {
var disableJarAssets by delegate(false)
var disableIndexAssets by delegate(false)
val indexAssetsTypes: MutableSet<IndexAssetsType> by setDelegate(mutableSetOf(*IndexAssetsType.VALUES))
val resourcePacks: List<ResourcePack> by listDelegate()
}

View File

@ -0,0 +1,6 @@
package de.bixilon.minosoft.config.profile.profiles.resources.assets.packs
class ResourcePack(
var type: ResourcePackType,
var path: String,
)

View File

@ -0,0 +1,11 @@
package de.bixilon.minosoft.config.profile.profiles.resources.assets.packs
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.assets.directory.DirectoryAssetsManager
import de.bixilon.minosoft.assets.file.ZipAssetsManager
enum class ResourcePackType(val creator: (ResourcePack) -> AssetsManager) {
ZIP({ ZipAssetsManager(it.path) }),
DIRECTORY({ DirectoryAssetsManager(it.path) }),
;
}

View File

@ -35,7 +35,7 @@ public class VersionParser extends CommandParser {
if (StringUtils.isBlank(rawVersionName)) {
throw new BlankStringCommandParseException(stringReader, rawVersionName);
}
Version version = Versions.getVersionByName(rawVersionName);
Version version = Versions.INSTANCE.get(rawVersionName);
if (version == null) {
throw new InvalidVersionCommandParseException(stringReader, rawVersionName);
}

View File

@ -254,20 +254,20 @@ class ItemStack(
val enchantmentList: MutableList<Map<String, Any>> = mutableListOf()
for ((enchantment, level) in enchantments) {
val enchantmentTag: MutableMap<String, Any> = mutableMapOf()
enchantmentTag[ENCHANTMENT_ID_TAG] = if (connection.version.isFlattened()) {
enchantmentTag[ENCHANTMENT_ID_TAG] = if (connection.version.flattened) {
enchantment.resourceLocation.full
} else {
connection.registries.enchantmentRegistry.getId(enchantment)
}
enchantmentTag[ENCHANTMENT_LEVEL_TAG] = if (connection.version.isFlattened()) {
enchantmentTag[ENCHANTMENT_LEVEL_TAG] = if (connection.version.flattened) {
level
} else {
level.toShort()
}
enchantmentList += enchantmentTag
}
if (connection.version.isFlattened()) {
if (connection.version.flattened) {
nbt[ENCHANTMENT_FLATTENING_TAG] = enchantmentList
} else {
nbt[ENCHANTMENT_PRE_FLATTENING_TAG] = enchantmentList

View File

@ -13,7 +13,7 @@
package de.bixilon.minosoft.data.language
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.assets.util.FileUtil.readAsString
import de.bixilon.minosoft.assets.util.FileUtil.readJsonObject
import de.bixilon.minosoft.data.registries.ResourceLocation
@ -53,10 +53,9 @@ class LanguageManager(
}
companion object {
const val FALLBACK_LANGUAGE = "en_US"
fun load(language: String, version: Version?, path: ResourceLocation = ResourceLocation("lang/")): LanguageManager {
val assetsManager = version?.jarAssetsManager ?: Minosoft.MINOSOFT_ASSETS_MANAGER
fun load(language: String, version: Version?, assetsManager: AssetsManager, path: ResourceLocation = ResourceLocation("lang/")): LanguageManager {
fun loadMinecraftLanguage(language: String): Language {
val data: MutableMap<ResourceLocation, String> = mutableMapOf()
@ -82,13 +81,12 @@ class LanguageManager(
val languages: MutableList<Language> = mutableListOf()
if (language != "en_US") {
if (language != FALLBACK_LANGUAGE) {
tryCatch(FileNotFoundException::class.java, executor = { languages += loadMinecraftLanguage(language) })
}
languages += loadMinecraftLanguage("en_US")
languages += loadMinecraftLanguage(FALLBACK_LANGUAGE)
return LanguageManager(languages)
}
}
}

View File

@ -1,21 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries
class RegistriesLoadingException : Exception {
constructor()
constructor(message: String?) : super(message)
constructor(message: String?, cause: Throwable?) : super(message, cause)
constructor(cause: Throwable?) : super(cause)
constructor(message: String?, cause: Throwable?, enableSuppression: Boolean, writableStackTrace: Boolean) : super(message, cause, enableSuppression, writableStackTrace)
}

View File

@ -127,8 +127,8 @@ class Registries {
}
}
fun load(version: Version, pixlyzerData: MutableMap<String, Any>) {
isFlattened = version.isFlattened()
fun load(version: Version, pixlyzerData: Map<String, Any>) {
isFlattened = version.flattened
blockStateRegistry.flattened = isFlattened
// pre init stuff
loadShapes(pixlyzerData["shapes"]?.compoundCast())
@ -157,7 +157,7 @@ class Registries {
entityTypeRegistry.rawInitialize(pixlyzerData["entities"]?.compoundCast(), this, EntityType)
motiveRegistry.rawInitialize(pixlyzerData["motives"]?.compoundCast(), this, Motive, version.isFlattened())
motiveRegistry.rawInitialize(pixlyzerData["motives"]?.compoundCast(), this, Motive, version.flattened)
soundEventRegistry.rawInitialize(pixlyzerData["sound_events"]?.compoundCast())
particleTypeRegistry.rawInitialize(pixlyzerData["particles"]?.compoundCast(), this, ParticleType)
materialRegistry.rawInitialize(pixlyzerData["materials"]?.compoundCast(), this, Material)
@ -166,8 +166,8 @@ class Registries {
biomeRegistry.rawInitialize(pixlyzerData["biomes"]?.compoundCast(), this, Biome)
dimensionRegistry.rawInitialize(pixlyzerData["dimensions"]?.compoundCast(), this, Dimension)
fluidRegistry.rawInitialize(pixlyzerData["fluids"]?.compoundCast(), this, Fluid)
blockRegistry.rawInitialize(pixlyzerData["blocks"]?.compoundCast(), this, Block, version.isFlattened(), Registry.MetaTypes.BITS_4)
itemRegistry.rawInitialize(pixlyzerData["items"]?.compoundCast(), this, Item, version.isFlattened(), Registry.MetaTypes.BITS_16)
blockRegistry.rawInitialize(pixlyzerData["blocks"]?.compoundCast(), this, Block, version.flattened, Registry.MetaTypes.BITS_4)
itemRegistry.rawInitialize(pixlyzerData["items"]?.compoundCast(), this, Item, version.flattened, Registry.MetaTypes.BITS_16)
blockEntityTypeRegistry.rawInitialize(pixlyzerData["block_entities"]?.compoundCast(), this, BlockEntityType)

View File

@ -0,0 +1,49 @@
package de.bixilon.minosoft.data.registries.registries
import de.bixilon.minosoft.assets.properties.version.AssetsVersionProperties
import de.bixilon.minosoft.assets.util.FileAssetsUtil
import de.bixilon.minosoft.assets.util.FileUtil
import de.bixilon.minosoft.assets.util.FileUtil.readMBFMap
import de.bixilon.minosoft.config.profile.profiles.resources.ResourcesProfile
import de.bixilon.minosoft.data.registries.versions.Version
import de.bixilon.minosoft.util.Util
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.compoundCast
import java.io.ByteArrayInputStream
import java.io.File
object RegistriesLoader {
fun load(profile: ResourcesProfile, version: Version): Registries {
// ToDo: Pre flattening support
val pixlyzerHash = AssetsVersionProperties[version]?.pixlyzerHash ?: throw IllegalStateException("$version has no pixlyzer data available!")
val pixlyzerData = getPixlyzerData(profile.source.pixlyzer, pixlyzerHash)
val registries = Registries()
registries.load(version, pixlyzerData)
return registries
}
private fun getPixlyzerData(url: String, hash: String): Map<String, Any> {
val path = FileAssetsUtil.getPath(hash)
val file = File(path)
if (file.exists()) {
// ToDo: Verify
return FileUtil.readFile(file, false).readMBFMap().compoundCast() ?: throw IllegalStateException("Could not read pixlyzer data!")
}
val savedHash = FileAssetsUtil.downloadAndGetAsset(Util.formatString(
url,
mapOf(
"hashPrefix" to hash.substring(0, 2),
"fullHash" to hash,
)
), false)
if (savedHash.first != hash) {
throw IllegalStateException("Data mismatch, expected $hash, got ${savedHash.first}")
}
return ByteArrayInputStream(savedHash.second).readMBFMap().compoundCast() ?: throw IllegalStateException("Invalid pixlyzer data!")
}
}

View File

@ -1,157 +1,45 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.versions
import com.google.common.collect.HashBiMap
import de.bixilon.mbf.MBFBinaryReader
import de.bixilon.minosoft.assets.minecraft.JarAssetsManager
import de.bixilon.minosoft.assets.minecraft.index.IndexAssetsManager
import de.bixilon.minosoft.assets.properties.version.AssetsVersionProperties
import de.bixilon.minosoft.assets.util.FileAssetsUtil
import de.bixilon.minosoft.assets.util.FileUtil
import de.bixilon.minosoft.config.profile.profiles.resources.ResourcesProfile
import de.bixilon.minosoft.data.registries.registries.Registries
import de.bixilon.minosoft.protocol.protocol.PacketTypes.C2S
import de.bixilon.minosoft.protocol.protocol.PacketTypes.S2C
import de.bixilon.minosoft.data.registries.registries.RegistriesLoader
import de.bixilon.minosoft.protocol.protocol.PacketTypes
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
import de.bixilon.minosoft.protocol.protocol.ProtocolVersions.V_15W31A
import de.bixilon.minosoft.util.CountUpAndDownLatch
import de.bixilon.minosoft.util.KUtil
import de.bixilon.minosoft.util.KUtil.decide
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.asCompound
@Deprecated(message = "Some refactoring needed")
data class Version(
var name: String,
class Version(
val name: String,
val versionId: Int,
val protocolId: Int,
val c2SPacketMapping: Map<ProtocolStates, HashBiMap<C2S, Int>>,
val s2CPacketMapping: Map<ProtocolStates, HashBiMap<S2C, Int>>,
val s2cPackets: Map<ProtocolStates, Array<PacketTypes.S2C>>,
val c2sPackets: Map<ProtocolStates, Array<PacketTypes.C2S>>,
) {
val sortingId: Int = (versionId == -1).decide(Int.MAX_VALUE, versionId)
val type: VersionTypes = VersionTypes[this]
var isLoaded = false
val registries: Registries = Registries()
lateinit var jarAssetsManager: JarAssetsManager
lateinit var indexAssetsManager: IndexAssetsManager
val type = VersionTypes[this]
var registries: Registries? = null
private set
fun getPacketById(state: ProtocolStates, command: Int): S2C? {
return s2CPacketMapping[state]?.inverse()?.get(command)
}
fun getPacketId(packet: C2S): Int? {
return c2SPacketMapping[packet.state]?.get(packet)
}
fun isFlattened(): Boolean {
return versionId >= ProtocolDefinition.FLATTING_VERSION_ID
}
private fun initializeAssetManger(latch: CountUpAndDownLatch) {
if (this::jarAssetsManager.isInitialized) {
@Synchronized
fun load(profile: ResourcesProfile) {
if (registries != null) {
// already loaded
return
}
if (!isFlattened() && versionId != ProtocolDefinition.PRE_FLATTENING_VERSION_ID) {
jarAssetsManager = Versions.PRE_FLATTENING_VERSION.jarAssetsManager
return
}
// ToDo
registries = RegistriesLoader.load(profile, this)
}
@Synchronized
fun load(latch: CountUpAndDownLatch) {
if (isLoaded) {
return
}
if (!isFlattened() && this !== Versions.PRE_FLATTENING_VERSION && !Versions.PRE_FLATTENING_VERSION.isLoaded) {
// no matter what, we need the version mapping for all pre flattening versions
try {
Versions.PRE_FLATTENING_VERSION.load(latch)
} catch (exception: Exception) {
Versions.PRE_FLATTENING_VERSION.unload()
Versions.PRE_FLATTENING_MAPPING = null
throw exception
}
}
latch.inc()
Log.log(LogMessageType.VERSION_LOADING, level = LogLevels.INFO) { "Loading registries for $this..." }
initializeAssetManger(latch)
val startTime = KUtil.time
if (versionId == ProtocolDefinition.PRE_FLATTENING_VERSION_ID) {
Versions.PRE_FLATTENING_MAPPING = registries
} else if (!isFlattened()) {
registries.parentRegistries = Versions.PRE_FLATTENING_MAPPING
}
val pixlyzerData = try {
MBFBinaryReader(FileUtil.readFile(FileAssetsUtil.getPath(AssetsVersionProperties[this]!!.pixlyzerHash ?: throw IllegalStateException("$this has no pixlyzer data!")), false)).readMBF().data.asCompound()
} catch (exception: Throwable) {
// should not happen, but if this version is not flattened, we can fallback to the flatten mappings. Some things might not work...
Log.log(LogMessageType.VERSION_LOADING, level = LogLevels.VERBOSE) { exception }
if (isFlattened()) {
throw exception
}
if (versionId == ProtocolDefinition.PRE_FLATTENING_VERSION_ID) {
Versions.PRE_FLATTENING_MAPPING = null
}
mutableMapOf()
}
latch.inc()
registries.load(this, pixlyzerData)
latch.dec()
if (pixlyzerData.isNotEmpty()) {
Log.log(LogMessageType.VERSION_LOADING, level = LogLevels.INFO) { "Loaded registries for $name in ${KUtil.time - startTime}ms" }
} else {
Log.log(LogMessageType.VERSION_LOADING, level = LogLevels.WARN) { "Could not load registries for ${name}. Some features might not work." }
}
isLoaded = true
latch.dec()
}
fun unload() {
registries.clear()
if (registries.parentRegistries == registries) {
registries.parentRegistries = null
}
isLoaded = false
}
override fun hashCode(): Int {
return versionId
}
override fun equals(other: Any?): Boolean {
if (super.equals(other)) {
return true
}
if (other !is Version) {
return false
}
return if (hashCode() != other.hashCode()) {
false
} else {
this.name == other.name
}
this.registries = null
}
override fun toString(): String {
return name
}
val hasOffhand = versionId >= V_15W31A
val flattened: Boolean = versionId >= ProtocolDefinition.FLATTING_VERSION_ID
val hasOffhand: Boolean = versionId >= V_15W31A
}

View File

@ -22,7 +22,7 @@ enum class VersionTypes {
private val SNAPSHOT_DETECT_REGEX = "(\\d{2}w\\d{2})[a-f]|(1.\\d{1,2}(.\\d+)?-?(rc|pre)\\d*)".toRegex()
@Deprecated(message = "Should be saved in the versions.json")
operator fun get(version: Version): VersionTypes {
operator fun get(version: de.bixilon.minosoft.data.registries.versions.Version): VersionTypes {
if (SNAPSHOT_DETECT_REGEX.matches(version.name)) {
return SNAPSHOT
}

View File

@ -1,112 +0,0 @@
/*
* Minosoft
* Copyright (C) 2020 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.data.registries.versions;
import com.google.common.collect.HashBiMap;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import de.bixilon.minosoft.data.registries.registries.Registries;
import de.bixilon.minosoft.protocol.protocol.PacketTypes;
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition;
import de.bixilon.minosoft.protocol.protocol.ProtocolStates;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.HashMap;
import java.util.Map;
@Deprecated
public class Versions {
public static final Version AUTOMATIC_VERSION = new Version("Automatic", -1, -1, new HashMap<>(), new HashMap<>());
public static final HashBiMap<Integer, @NonNull Version> VERSION_ID_MAP = HashBiMap.create(500);
private static final HashBiMap<Integer, @NonNull Version> VERSION_PROTOCOL_ID_MAP = HashBiMap.create(500);
private static final HashBiMap<String, @NonNull Version> VERSION_NAME_MAP = HashBiMap.create(500);
public static Registries PRE_FLATTENING_MAPPING;
public static Version PRE_FLATTENING_VERSION;
public static Version getVersionById(int versionId) {
return VERSION_ID_MAP.get(versionId);
}
public static Version getVersionByProtocolId(int protocolId) {
return VERSION_PROTOCOL_ID_MAP.get(protocolId);
}
@Nullable
public static Version getVersionByName(@Nullable final String name) {
if ("automatic".equals(name)) {
return AUTOMATIC_VERSION;
}
return VERSION_NAME_MAP.get(name);
}
public static void loadAvailableVersions(JsonObject json) {
for (String versionId : json.keySet()) {
loadVersion(json, versionId);
}
}
private static Version loadVersion(JsonObject json, String versionIdString) {
JsonObject versionJson = json.getAsJsonObject(versionIdString);
String versionName = versionJson.get("name").getAsString();
int versionId = Integer.parseInt(versionIdString);
if (VERSION_ID_MAP.containsKey(versionId)) {
// already loaded, skip
return VERSION_ID_MAP.get(versionId);
}
Map<ProtocolStates, HashBiMap<PacketTypes.C2S, Integer>> c2sMapping;
Map<ProtocolStates, HashBiMap<PacketTypes.S2C, Integer>> s2cMapping;
if (versionJson.get("mapping").isJsonPrimitive()) {
// inherits or copies mapping from an other version
Version parent = VERSION_ID_MAP.get(versionJson.get("mapping").getAsInt());
if (parent == null) {
parent = loadVersion(json, versionJson.get("mapping").getAsString());
}
c2sMapping = parent.getC2SPacketMapping();
s2cMapping = parent.getS2CPacketMapping();
} else {
JsonObject mappingJson = versionJson.getAsJsonObject("mapping");
c2sMapping = new HashMap<>();
for (JsonElement packetElement : mappingJson.getAsJsonArray("c2s")) {
PacketTypes.C2S packet = PacketTypes.C2S.valueOf(packetElement.getAsString());
if (!c2sMapping.containsKey(packet.getState())) {
c2sMapping.put(packet.getState(), HashBiMap.create(30));
}
c2sMapping.get(packet.getState()).put(packet, c2sMapping.get(packet.getState()).size());
}
s2cMapping = new HashMap<>();
for (JsonElement packetElement : mappingJson.getAsJsonArray("s2c")) {
PacketTypes.S2C packet = PacketTypes.S2C.valueOf(packetElement.getAsString());
if (!s2cMapping.containsKey(packet.getState())) {
s2cMapping.put(packet.getState(), HashBiMap.create(100));
}
s2cMapping.get(packet.getState()).put(packet, s2cMapping.get(packet.getState()).size());
}
}
int protocolId = versionId;
if (versionJson.has("protocol_id")) {
protocolId = versionJson.get("protocol_id").getAsInt();
}
Version version = new Version(versionName, versionId, protocolId, c2sMapping, s2cMapping);
VERSION_ID_MAP.put(version.getVersionId(), version);
VERSION_PROTOCOL_ID_MAP.put(version.getProtocolId(), version);
VERSION_NAME_MAP.put(version.getName(), version);
if (version.getVersionId() == ProtocolDefinition.PRE_FLATTENING_VERSION_ID) {
PRE_FLATTENING_VERSION = version;
}
return version;
}
}

View File

@ -0,0 +1,104 @@
package de.bixilon.minosoft.data.registries.versions
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.assets.util.FileUtil.readJson
import de.bixilon.minosoft.protocol.protocol.PacketTypes
import de.bixilon.minosoft.protocol.protocol.ProtocolStates
import de.bixilon.minosoft.util.KUtil.toInt
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.KUtil.unsafeCast
object Versions : Iterable<Version> {
private val VERSIONS_INDEX = "minosoft:mapping/versions.json".toResourceLocation()
val AUTOMATIC = Version("Automatic", -1, -1, mapOf(), mapOf())
private val VERSIONS_BY_NAME: MutableMap<String, Version> = mutableMapOf()
private val VERSIONS_BY_ID: MutableMap<Int, Version> = mutableMapOf()
private val VERSIONS_BY_PROTOCOL: MutableMap<Int, Version> = mutableMapOf()
private fun addVersion(version: Version) {
VERSIONS_BY_NAME.put(version.name, version)?.let { throw IllegalStateException("Duplicated version name: ${version.name}") }
VERSIONS_BY_ID.put(version.versionId, version)?.let { throw IllegalStateException("Duplicated version id: ${version.name}") }
VERSIONS_BY_PROTOCOL.put(version.protocolId, version)?.let { throw IllegalStateException("Duplicated protocol id: ${version.name}") }
}
@Synchronized
fun load() {
val index: Map<String, Map<String, Any>> = Minosoft.MINOSOFT_ASSETS_MANAGER[VERSIONS_INDEX].readJson()
fun loadVersion(versionId: Int, data: Map<String, Any> = index[versionId.toString()]!!): Version {
VERSIONS_BY_ID[versionId]?.let { return it }
val s2cPackets: Map<ProtocolStates, Array<PacketTypes.S2C>>
val c2sPackets: Map<ProtocolStates, Array<PacketTypes.C2S>>
when (val mapping = data["mapping"]) {
is Int -> {
val mappingVersion = loadVersion(mapping)
s2cPackets = mappingVersion.s2cPackets
c2sPackets = mappingVersion.c2sPackets
}
is Map<*, *> -> {
mapping["s2c"].unsafeCast<List<String>>().let {
val map: MutableMap<ProtocolStates, MutableList<PacketTypes.S2C>> = mutableMapOf()
for (packetName in it) {
val packetType = PacketTypes.S2C[packetName]
map.getOrPut(packetType.state) { mutableListOf() } += packetType
}
val mapOut: MutableMap<ProtocolStates, Array<PacketTypes.S2C>> = mutableMapOf()
for ((state, types) in map) {
mapOut[state] = types.toTypedArray()
}
s2cPackets = mapOut.toMap()
}
mapping["c2s"].unsafeCast<List<String>>().let {
val map: MutableMap<ProtocolStates, MutableList<PacketTypes.C2S>> = mutableMapOf()
for (packetName in it) {
val packetType = PacketTypes.C2S[packetName]
map.getOrPut(packetType.state) { mutableListOf() } += packetType
}
val mapOut: MutableMap<ProtocolStates, Array<PacketTypes.C2S>> = mutableMapOf()
for ((state, types) in map) {
mapOut[state] = types.toTypedArray()
}
c2sPackets = mapOut.toMap()
}
}
else -> TODO("Can not create version mapping $mapping")
}
val version = Version(
name = data["name"].toString(),
versionId = versionId,
protocolId = data["protocol_id"]?.toInt() ?: versionId,
s2cPackets = s2cPackets,
c2sPackets = c2sPackets,
)
addVersion(version)
return version
}
for ((versionId, data) in index) {
loadVersion(versionId.toInt(), data)
}
}
operator fun get(name: String?): Version? {
return VERSIONS_BY_NAME[name]
}
fun getById(versionId: Int): Version? {
return VERSIONS_BY_ID[versionId]
}
fun getByProtocol(protocolId: Int): Version? {
return VERSIONS_BY_PROTOCOL[protocolId]
}
override fun iterator(): Iterator<Version> {
return VERSIONS_BY_NAME.values.iterator()
}
}

View File

@ -129,7 +129,7 @@ class ErosCrashReport : JavaFXWindowController() {
}
})
tryCatch(executor = {
for (connection in PlayConnection.ACTIVE_CONENCTIONS.toSynchronizedSet()) {
for (connection in PlayConnection.ACTIVE_CONNECTIONS.toSynchronizedSet()) {
connection.disconnect()
}
})

View File

@ -75,7 +75,7 @@ class ServerModifyDialog(
private fun refreshVersions() {
val selected = forcedVersionFX.selectionModel.selectedItem
forcedVersionFX.items.clear()
for (version in Versions.VERSION_ID_MAP.values) {
for (version in Versions) {
if (version.type == VersionTypes.RELEASE && !showReleasesFX.isSelected) {
continue
}
@ -85,14 +85,14 @@ class ServerModifyDialog(
forcedVersionFX.items += version
}
forcedVersionFX.items += Versions.AUTOMATIC_VERSION
forcedVersionFX.items += Versions.AUTOMATIC
forcedVersionFX.items.sortByDescending { it.sortingId }
if (forcedVersionFX.items.contains(selected)) {
forcedVersionFX.selectionModel.select(selected)
} else {
forcedVersionFX.selectionModel.select(Versions.AUTOMATIC_VERSION)
forcedVersionFX.selectionModel.select(Versions.AUTOMATIC)
}
}
@ -135,7 +135,7 @@ class ServerModifyDialog(
super.updateItem(version, empty)
version ?: return
text = if (version == Versions.AUTOMATIC_VERSION) {
text = if (version == Versions.AUTOMATIC) {
Minosoft.LANGUAGE_MANAGER.translate(VERSION_AUTOMATIC).message
} else {
"${version.name} (${version.type.name.lowercase()})"
@ -146,12 +146,12 @@ class ServerModifyDialog(
refreshVersions()
if (server == null) {
forcedVersionFX.selectionModel.select(Versions.AUTOMATIC_VERSION)
forcedVersionFX.selectionModel.select(Versions.AUTOMATIC)
// add
descriptionFX.text = ADD_DESCRIPTION
modifyServerButtonFX.ctext = ADD_UPDATE_BUTTON
} else {
forcedVersionFX.selectionModel.select(server.forcedVersion ?: Versions.AUTOMATIC_VERSION)
forcedVersionFX.selectionModel.select(server.forcedVersion ?: Versions.AUTOMATIC)
descriptionFX.text = EDIT_DESCRIPTION
modifyServerButtonFX.ctext = EDIT_UPDATE_BUTTON
@ -173,7 +173,7 @@ class ServerModifyDialog(
if (modifyServerButtonFX.isDisable) {
return
}
val forcedVersion = (forcedVersionFX.selectionModel.selectedItem == Versions.AUTOMATIC_VERSION).decide(null) { forcedVersionFX.selectionModel.selectedItem }
val forcedVersion = (forcedVersionFX.selectionModel.selectedItem == Versions.AUTOMATIC).decide(null) { forcedVersionFX.selectionModel.selectedItem }
DefaultThreadPool += { onUpdate(serverNameFX.text.isBlank().decide({ serverAddressFX.text.toString() }, { serverNameFX.text.trim() }), serverAddressFX.text, forcedVersion, profiles) }
stage.close()
}

View File

@ -130,7 +130,7 @@ class WorldRenderer(
override fun init() {
val asset = AssetsVersionProperties[connection.version]!!
val zip = FileUtil.readFile(FileAssetsUtil.getPath(asset.clientJarHash)).readArchive()
val zip = FileUtil.readFile(FileAssetsUtil.getPath(asset.jarAssetsHash)).readArchive()
val modelLoader = ModelLoader(zip, renderWindow)
modelLoader.load()

View File

@ -13,8 +13,8 @@
package de.bixilon.minosoft.protocol.network.connection.play
import de.bixilon.minosoft.Minosoft
import de.bixilon.minosoft.assets.multi.PriorityAssetsManager
import de.bixilon.minosoft.assets.AssetsLoader
import de.bixilon.minosoft.assets.AssetsManager
import de.bixilon.minosoft.config.profile.ConnectionProfiles
import de.bixilon.minosoft.data.ChatTextPositions
import de.bixilon.minosoft.data.accounts.Account
@ -24,7 +24,6 @@ import de.bixilon.minosoft.data.language.LanguageManager
import de.bixilon.minosoft.data.physics.CollisionDetector
import de.bixilon.minosoft.data.player.LocalPlayerEntity
import de.bixilon.minosoft.data.player.tab.TabList
import de.bixilon.minosoft.data.registries.RegistriesLoadingException
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.registries.ResourceLocationAble
import de.bixilon.minosoft.data.registries.recipes.Recipes
@ -83,7 +82,7 @@ class PlayConnection(
@Deprecated(message = "PacketSender is deprecated")
val sender = PacketSender(this)
val serverInfo = ServerInfo()
lateinit var assetsManager: PriorityAssetsManager
lateinit var assetsManager: AssetsManager
private set
val tags: MutableMap<ResourceLocation, Map<ResourceLocation, Tag<Any>>> = synchronizedMapOf()
lateinit var language: LanguageManager
@ -129,7 +128,7 @@ class PlayConnection(
when (value) {
ProtocolStates.HANDSHAKING -> {
state = PlayConnectionStates.HANDSHAKING
ACTIVE_CONENCTIONS += this
ACTIVE_CONNECTIONS += this
for ((validators, invokers) in GlobalEventMaster.specificEventInvokers) {
var valid = false
for (serverAddress in validators) {
@ -206,7 +205,7 @@ class PlayConnection(
TimeWorker.removeTask(randomTickTask)
}
state = PlayConnectionStates.DISCONNECTED
ACTIVE_CONENCTIONS -= this
ACTIVE_CONNECTIONS -= this
}
else -> {
}
@ -218,16 +217,20 @@ class PlayConnection(
try {
state = PlayConnectionStates.LOADING
fireEvent(RegistriesLoadEvent(this, registries, RegistriesLoadEvent.States.PRE))
version.load(latch) // ToDo: show gui loader
assetsManager = PriorityAssetsManager(Minosoft.MINOSOFT_ASSETS_MANAGER)
language = LanguageManager.load(profiles.connection.language ?: profiles.eros.general.language, version)
version.load(profiles.resources)
registries.parentRegistries = version.registries
assetsManager = AssetsLoader.create(profiles.resources, version)
Log.log(LogMessageType.ASSETS, LogLevels.INFO) { "Downloading and verifying assets. This might take a while..." }
assetsManager.load(latch)
Log.log(LogMessageType.ASSETS, LogLevels.INFO) { "Assets verified!" }
language = LanguageManager.load(profiles.connection.language ?: profiles.eros.general.language, version, assetsManager)
fireEvent(RegistriesLoadEvent(this, registries, RegistriesLoadEvent.States.POST))
player = LocalPlayerEntity(account, this)
if (!RunConfiguration.DISABLE_RENDERING) {
assetsManager.add(version.jarAssetsManager)
assetsManager.add(version.indexAssetsManager)
val renderer = Rendering(this)
this.rendering = renderer
val renderLatch = CountUpAndDownLatch(0, latch)
@ -239,26 +242,28 @@ class PlayConnection(
state = PlayConnectionStates.ESTABLISHING
} catch (exception: Throwable) {
Log.log(LogMessageType.VERSION_LOADING, level = LogLevels.FATAL) { exception }
Log.log(LogMessageType.VERSION_LOADING, level = LogLevels.FATAL) { "Could not load version $version. This version seems to be unsupported" }
version.unload()
error = RegistriesLoadingException("Registries could not be loaded", exception)
if (this::assetsManager.isInitialized) {
assetsManager.unload()
}
error = exception
retry = false
}
latch.dec()
}
override fun getPacketId(packetType: PacketTypes.C2S): Int {
return version.getPacketId(packetType) ?: Protocol.getPacketId(packetType) ?: error("Can not find packet $packetType for $version")
// ToDo: Improve speed
return version.c2sPackets[packetType.state]?.indexOf(packetType) ?: Protocol.getPacketId(packetType) ?: error("Can not find packet $packetType for $version")
}
override fun getPacketById(packetId: Int): PacketTypes.S2C {
return version.getPacketById(protocolState, packetId) ?: Protocol.getPacketById(protocolState, packetId) ?: let {
return version.s2cPackets[protocolState]?.getOrNull(packetId) ?: Protocol.getPacketById(protocolState, packetId) ?: let {
// wtf, notchain sends play disconnect packet in login state...
if (protocolState != ProtocolStates.LOGIN) {
return@let null
}
val playPacket = version.getPacketById(ProtocolStates.PLAY, packetId)
val playPacket = version.s2cPackets[ProtocolStates.PLAY]?.getOrNull(packetId)
if (playPacket == PacketTypes.S2C.PLAY_KICK) {
return@let playPacket
}
@ -301,6 +306,6 @@ class PlayConnection(
}
companion object {
val ACTIVE_CONENCTIONS: MutableSet<PlayConnection> = synchronizedSetOf()
val ACTIVE_CONNECTIONS: MutableSet<PlayConnection> = synchronizedSetOf()
}
}

View File

@ -64,7 +64,7 @@ class ClientSettingsManager(
return
}
this.language = language
connection.language = LanguageManager.load(language, connection.version)
connection.language = LanguageManager.load(language, connection.version, connection.assetsManager)
sendClientSettings()
}

View File

@ -124,7 +124,7 @@ class StatusConnection(
when (value) {
ProtocolStates.HANDSHAKING -> {
state = StatusConnectionStates.HANDSHAKING
network.sendPacket(HandshakeC2SP(realAddress!!, ProtocolStates.STATUS, Versions.AUTOMATIC_VERSION.protocolId))
network.sendPacket(HandshakeC2SP(realAddress!!, ProtocolStates.STATUS, Versions.AUTOMATIC.protocolId))
protocolState = ProtocolStates.STATUS
}
ProtocolStates.STATUS -> {

View File

@ -24,14 +24,14 @@ import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
class ClientSettingsC2SP(
val locale: String = "en_US",
var chatColors: Boolean = true,
val viewDistance: Int = 10,
val chatMode: ChatModes = ChatModes.EVERYTHING,
val skinParts: Array<SkinParts> = SkinParts.VALUES,
val mainArm: Arms = Arms.RIGHT,
val disableTextFiltering: Boolean = true,
val allowListing: Boolean = true,
val locale: String,
var chatColors: Boolean,
val viewDistance: Int,
val chatMode: ChatModes,
val skinParts: Array<SkinParts>,
val mainArm: Arms,
val disableTextFiltering: Boolean,
val allowListing: Boolean,
) : PlayC2SPacket {
override fun write(buffer: PlayOutByteBuffer) {

View File

@ -31,7 +31,7 @@ class ServerStatusResponseS2CP(buffer: InByteBuffer) : StatusS2CPacket() {
override fun handle(connection: StatusConnection) {
connection.lastServerStatus = status
val version: Version? = Versions.getVersionByProtocolId(status.protocolId ?: -1)
val version: Version? = Versions.getByProtocol(status.protocolId ?: -1)
if (version == null) {
Log.log(LogMessageType.NETWORK_STATUS, LogLevels.WARN) { "Server is running on unknown version (protocolId=${status.protocolId})" }
} else {

View File

@ -49,6 +49,8 @@ import de.bixilon.minosoft.protocol.packets.s2c.play.scoreboard.teams.TeamsS2CF
import de.bixilon.minosoft.protocol.packets.s2c.play.title.*
import de.bixilon.minosoft.protocol.packets.s2c.status.ServerStatusResponseS2CP
import de.bixilon.minosoft.protocol.packets.s2c.status.StatusPongS2CP
import de.bixilon.minosoft.util.KUtil
import de.bixilon.minosoft.util.enum.ValuesEnum
class PacketTypes {
@ -115,8 +117,10 @@ class PacketTypes {
val state: ProtocolStates = ProtocolStates.valueOf(name.split("_".toRegex()).toTypedArray()[0])
companion object {
companion object : ValuesEnum<C2S> {
private val MAPPING: Map<Class<out C2SPacket>, C2S>
override val VALUES: Array<C2S> = values()
override val NAME_MAP: Map<String, C2S> = KUtil.getEnumValues(VALUES)
init {
val mapping: MutableMap<Class<out C2SPacket>, C2S> = mutableMapOf()
@ -288,5 +292,11 @@ class PacketTypes {
val state: ProtocolStates = ProtocolStates.valueOf(name.split("_".toRegex()).toTypedArray()[0])
companion object : ValuesEnum<S2C> {
override val VALUES: Array<S2C> = values()
override val NAME_MAP: Map<String, S2C> = KUtil.getEnumValues(VALUES)
}
}
}

View File

@ -116,7 +116,7 @@ class PlayInByteBuffer : InByteBuffer {
}
val count = readUnsignedByte()
var metaData = 0
if (connection.version.isFlattened()) {
if (!connection.version.flattened) {
metaData = readUnsignedShort()
}
val nbt = readNBTTag(versionId < V_14W28B)?.compoundCast()

View File

@ -40,11 +40,11 @@ object AutoConnect {
// ToDo: Show those connections in eros
val split = connectString.split(',')
val address = split[0]
val version = Versions.getVersionByName(split.getOrNull(1) ?: "automatic") ?: throw IllegalArgumentException("Auto connect: Version not found!")
val version = Versions[split.getOrNull(1) ?: "automatic"] ?: throw IllegalArgumentException("Auto connect: Version not found!")
val accountProfile = AccountProfileManager.selected
val account = accountProfile.entries[split.getOrNull(2)] ?: accountProfile.selected ?: throw RuntimeException("Auto connect: Account not found! Have you started normal before or added an account?")
if (version == Versions.AUTOMATIC_VERSION) {
if (version == Versions.AUTOMATIC) {
Log.log(LogMessageType.AUTO_CONNECT, LogLevels.INFO) { "Pinging server to get version..." }
val ping = StatusConnection(address)
ping.ping()

View File

@ -53,7 +53,7 @@ object KUtil {
val ret: MutableMap<String, T> = mutableMapOf()
for (value in values) {
ret[value.name.lowercase(Locale.getDefault())] = value
ret[value.name.lowercase()] = value
if (value is AliasableEnum) {
for (name in value.names) {

View File

@ -30,7 +30,7 @@ object ShutdownManager {
fun shutdown(message: String? = null, reason: ShutdownReasons = ShutdownReasons.UNKNOWN) {
Log.log(LogMessageType.GENERAL, LogLevels.INFO) { "Shutting down..." }
for (connection in PlayConnection.ACTIVE_CONENCTIONS.toSynchronizedSet()) {
for (connection in PlayConnection.ACTIVE_CONNECTIONS.toSynchronizedSet()) {
connection.disconnect()
}
FileWatcherService.stop()

View File

@ -36,7 +36,6 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;
import java.util.regex.Pattern;
import java.util.zip.*;
@ -46,7 +45,7 @@ public final class Util {
public static final char[] RANDOM_STRING_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".toCharArray();
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
public static final Gson GSON = new Gson();
private static final Random THREAD_LOCAL_RANDOM = ThreadLocalRandom.current();
private static final Random RANDOM = new Random();
private static final Field JSON_READER_POS_FIELD;
private static final Field JSON_READER_LINE_START_FIELD;
@ -251,11 +250,11 @@ public final class Util {
}
public static char getRandomChar(char[] chars) {
return chars[(THREAD_LOCAL_RANDOM.nextInt(chars.length))];
return chars[(RANDOM.nextInt(chars.length))];
}
public static char getRandomChar() {
return (char) THREAD_LOCAL_RANDOM.nextInt();
return (char) RANDOM.nextInt();
}
public static String getStringBetween(String search, String first, String second) {

View File

@ -22,7 +22,7 @@ interface ValuesEnum<T : Enum<*>> {
}
operator fun get(name: String): T {
return NAME_MAP[name] ?: throw IllegalArgumentException("Can not find enum value: $name")
return NAME_MAP[name.lowercase()] ?: throw IllegalArgumentException("Can not find enum value: $name")
}
fun next(current: T): T {