Code Monkey home page Code Monkey logo

Comments (18)

Evyde avatar Evyde commented on June 28, 2024 2

好快的修!
但是多平台查询好像没实现?在返回103之后就直接错误了,要不要弄个循环试试?
@Shennoter

from apexranklookup.

Evyde avatar Evyde commented on June 28, 2024 1

API正常,毕竟是昨天刚刚申请的。

原因分析

刚刚看了下代码,大概清楚是怎么回事了:
首先,当玩家“不存在”时(应该是存在的,比如XBox用户),API会返回一个HTTP Code为200 OK的Error Message:

{"Error":"Player XXXXX not found (code 103 - skipping origin backup api)"}

如图:
image
utils/GetResponse.kt中的Line 34-44只对Response的HTTP Code进行了判断。

return when(response.code){
200 -> Pair(0, body)
400 -> Pair(1, "请重试")
403 -> Pair(1, "API key无权限或不存在")
404 -> Pair(1, "玩家不存在")
405 -> Pair(1, "玩家存在但从未玩过APEX")
410 -> Pair(1, "未知平台")
429 -> Pair(1, "API过热,请稍后再试")
500 -> Pair(1, "API服务器内部错误")
else -> Pair(1, "未知错误")
}

并且拿到getRes()返回值的playerStat()方法只对构造的返回代码进行了校验,没有进行额外的判断。
if (requestStr.first == 0) return@breaking

换句话说,getRes()返回的body是被信任的,但实际不应该如此。
然后就是playerStat()的Line 32,直接调用Gson()进行构造:
val res = Gson().fromJson(requestStr.second, ApexResponsePlayer::class.java)

Gson在找不到对应key的value时,会将相应变量置null,但由于您使用了ApexResponsePlayer类,res变量本身不为null,同时您也没有对Gson的返回值进行判断,而是直接调用了playerPicturMode()
playerPicturMode(res,playerid,image)

这个方法并没有对传入的res变量做非null检查,仅进行了类型约束,所以会在访问res.global的时候抛出NullPointerException。
val rank: BufferedImage = ImageCache("rank_"+res.global.rank.rankName+res.global.rank.rankDiv,res.global.rank.rankImg)

鉴于我昨天是正常查询了的,所以我估计API在首次探测的时候不会检测平台,这也是当platform设置为PC能够正常查询的原因。但当API那边有了缓存之后,用platform为PC的值就查不到了。

修改建议

  • 对所有返回值进行非Null检查,类似:
try {
    if (res == null || res.global == null || res.global.rank == null || res.global.rank.rankName == null) {
        throw new RuntimeException();
    }
} catch (RuntimeException re) {
    return ...;
}

可能Kotlin有更好的做法,我不了解Kotlin就不做评价了。
我记得Gson也可以定义找到null时候的行为。

  • 在查询不到时更换不同平台进行查询

from apexranklookup.

Shennoter avatar Shennoter commented on June 28, 2024 1

原来是这样,感谢,其实这个返回状态码200错误信息的问题,我在v1.4.0以前的版本是用一个ApexResponseError的类来处理的(很简单粗暴)

data class ApexResponseError(
val Error: String
)

if (requestStr.contains("Error")){
var errorInfo = ""
val res = Gson().fromJson(requestStr, ApexResponseError::class.java)
errorInfo = "查询出错:" + res.Error
return errorInfo
}

在v1.4.0写了一个专门用来请求数据的方法后就忘记这种情况的处理了,在v1.5.1发布后其实我有注意到这个问题,并且已经在idea写好了准备多积累几个bug一起发布,由于没有把这个问题和这个bug联系起来,我就没有单独更新

  return when(response.code){
      200 -> {
          if (body?.contains("Error") == true) {
              Pair(1,"错误:" + body.split("\"")[3]) //{"Error":"Player /apexmap not found (code 103 - skipping origin backup api)"} 总有人会输入一些意料之外的数据
          } else {
              Pair(0, body)
          }
      }
      400 -> Pair(1, "请重试")
      403 -> Pair(1, "API key无权限或不存在")
      404 -> Pair(1, "玩家不存在")
      405 -> Pair(1, "玩家存在但从未玩过APEX")
      410 -> Pair(1, "未知平台")
      429 -> Pair(1, "API过热,请稍后再试")
      500 -> Pair(1, "API服务器内部错误")
      else -> Pair(1, "未知错误")
  }

您的两个建议将会在下次更新实现,谢谢!

from apexranklookup.

Shennoter avatar Shennoter commented on June 28, 2024

浏览器输入
https://api.mozambiquehe.re/maprotation?version=2&auth= ${apikey}
检查一下apikey的有效性

其实这个问题我也不是很清楚为什么。从StackTrace来看是完成了接收响应包这一步,然后到了生成图片读取响应包中的数据时读到了null。但是我在从发请求到判断响应包状态码都有相应的异常处理,一个空数据是怎样绕过这些异常处理来到图片生成环节的,我还没搞清楚。其他人也偶尔会有这种问题,有的人是config里的User.json多了个问号,大部分都是过一段时间突然就没问题,很玄学
主要是这个问题我没碰到过,也没办法复现

from apexranklookup.

Evyde avatar Evyde commented on June 28, 2024

原来是这样,感谢,其实这个返回状态码200错误信息的问题,我在v1.4.0以前的版本是用一个ApexResponseError的类来处理的(很简单粗暴)

data class ApexResponseError(
val Error: String
)

if (requestStr.contains("Error")){
var errorInfo = ""
val res = Gson().fromJson(requestStr, ApexResponseError::class.java)
errorInfo = "查询出错:" + res.Error
return errorInfo
}

在v1.4.0写了一个专门用来请求数据的方法后就忘记这种情况的处理了,在v1.5.1发布后其实我有注意到这个问题,并且已经在idea写好了准备多积累几个bug一起发布,由于没有把这个问题和这个bug联系起来,我就没有单独更新

  return when(response.code){
      200 -> {
          if (body?.contains("Error") == true) {
              Pair(1,"错误:" + body.split("\"")[3]) //{"Error":"Player /apexmap not found (code 103 - skipping origin backup api)"} 总有人会输入一些意料之外的数据
          } else {
              Pair(0, body)
          }
      }
      400 -> Pair(1, "请重试")
      403 -> Pair(1, "API key无权限或不存在")
      404 -> Pair(1, "玩家不存在")
      405 -> Pair(1, "玩家存在但从未玩过APEX")
      410 -> Pair(1, "未知平台")
      429 -> Pair(1, "API过热,请稍后再试")
      500 -> Pair(1, "API服务器内部错误")
      else -> Pair(1, "未知错误")
  }

您的两个建议将会在下次更新实现,谢谢!

感谢及时修复。

from apexranklookup.

Shennoter avatar Shennoter commented on June 28, 2024

好快的修! 但是多平台查询好像没实现?在返回103之后就直接错误了,要不要弄个循环试试? @Shennoter

有其他平台的id吗,测试一下

from apexranklookup.

Evyde avatar Evyde commented on June 28, 2024

好快的修! 但是多平台查询好像没实现?在返回103之后就直接错误了,要不要弄个循环试试? @Shennoter

有其他平台的id吗,测试一下

没找到您的私聊方式,放到了这里(阅后即焚)。

from apexranklookup.

Shennoter avatar Shennoter commented on June 28, 2024

我在我的机器人试了一下,正常输出了图片,在PS排行榜找了排名第一的id试了一下,也可以正常输出图片(确认过帐号是没有PC平台的)

from apexranklookup.

Evyde avatar Evyde commented on June 28, 2024

我在我的机器人试了一下,正常输出了图片,在PS排行榜找了排名第一的id试了一下,也可以正常输出图片(确认过帐号是没有PC平台的)

我说的情况就是这个,第一次应该是服务器没有缓存所以用PC的可以查,但是第二次之后就不行了,提示未找到。必须把platform参数换成对应平台才行。

from apexranklookup.

Shennoter avatar Shennoter commented on June 28, 2024

emm,我刚刚连续查询了五次都是有正常返回的,又试了一下中途查询其他id再继续查询此id,都是正常输出

if(requestStr.first == 1){ //如出错则首先查找其他平台
run breaking@{
listOf("PS4", "X1").forEach { platform ->
url = "https://api.mozambiquehe.re/bridge?version=5&platform=$platform&player=$id&auth=${Config.apiKey}"
requestStr = getRes(url)
if (requestStr.first == 0) return@breaking
}
}

不过确实也可以通过加一个循环来代替额外apikey的作用
突然又想到,如果在有几个人同时查询的情况下,加循环可能会导致死锁吧,注册过的apikey查询频率限制是2次/秒(好像又不会)

from apexranklookup.

Evyde avatar Evyde commented on June 28, 2024

emm,我刚刚连续查询了五次都是有正常返回的,又试了一下中途查询其他id再继续查询此id,都是正常输出

if(requestStr.first == 1){ //如出错则首先查找其他平台
run breaking@{
listOf("PS4", "X1").forEach { platform ->
url = "https://api.mozambiquehe.re/bridge?version=5&platform=$platform&player=$id&auth=${Config.apiKey}"
requestStr = getRes(url)
if (requestStr.first == 0) return@breaking
}
}

不过确实也可以通过加一个循环来代替额外apikey的作用
突然又想到,如果在有几个人同时查询的情况下,加循环可能会导致死锁吧,注册过的apikey查询频率限制是2次/秒(好像又不会)

PS榜这几位PC号和PS4号都用的一个账号名,但是等级不一样,可以看到不同的内容:
image
image

所以和我说的不太一样。我说的是那种Xbox/PS Only的情况,比如这个人:
dino771008
image
PS有他的信息,但是PC没有,提示102
image
所以其实修改建议是把全平台查一遍,然后发三张图或者三块儿拼成一个图,这样可以避免同一个用户名在不同平台有不同进度的问题。

from apexranklookup.

Shennoter avatar Shennoter commented on June 28, 2024

那我下次直接给/apexid加一个平台参数,不输入的话就默认是PC
config加一个默认平台,应对不同平台的用户群

from apexranklookup.

Evyde avatar Evyde commented on June 28, 2024

那我下次直接给/apexid加一个平台参数,不输入的话就默认是PC

这看您了,我觉得可能查三个平台然后发三张图更简单一点(

from apexranklookup.

Evyde avatar Evyde commented on June 28, 2024

哦对了,/apexid看起来正常,我一直都是拿/apexadd id试的。这个会报错。

from apexranklookup.

Shennoter avatar Shennoter commented on June 28, 2024

这下应该没问题了吧

from apexranklookup.

Evyde avatar Evyde commented on June 28, 2024

这下应该没问题了吧

2022-06-16 22:55:59 W/ApexLookUp: Exception in executing command `[mirai:source:[],[]]/apexadd id XXXXX`
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:226)
        at com.google.gson.Gson.fromJson(Gson.java:932)
        at com.google.gson.Gson.fromJson(Gson.java:897)
        at com.google.gson.Gson.fromJson(Gson.java:846)
        at com.google.gson.Gson.fromJson(Gson.java:817)
        at ranklookup-1.5.3.mirai.jar//pers.shennoter.PlayerListenerKt.playerStatListener(PlayerListener.kt:47)
        at ranklookup-1.5.3.mirai.jar//pers.shennoter.PlayerListenerKt$playerStatListener$1.invokeSuspend(PlayerListener.kt)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Caused by: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 1 path $
        at com.google.gson.stream.JsonReader.beginObject(JsonReader.java:384)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:215)
        ... 12 more

from apexranklookup.

Shennoter avatar Shennoter commented on June 28, 2024

release重新上传了

from apexranklookup.

Evyde avatar Evyde commented on June 28, 2024

release重新上传了

应该没问题了,测了几天都是正常使用的。

from apexranklookup.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.