Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unified Messaging v2 #282

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ plugins {

allprojects {
group = "in.dragonbra"
version = "1.5.1"
version = "1.6.0-SNAPSHOT"
}

repositories {
Expand Down Expand Up @@ -116,7 +116,6 @@ dependencies {
implementation(libs.commons.validator)
implementation(libs.gson)
implementation(libs.kotlin.coroutines)
implementation(libs.kotlin.stdib)
implementation(libs.okHttp)
implementation(libs.protobuf.java)
implementation(libs.webSocket)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package `in`.dragonbra.generators.rpc.parser

import com.squareup.kotlinpoet.*
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import `in`.dragonbra.generators.rpc.RpcGenTask
import java.io.File
import java.util.*
Expand All @@ -10,38 +9,28 @@ class ProtoParser(private val outputDir: File) {

private companion object {
private const val RPC_PACKAGE = "in.dragonbra.javasteam.rpc"
private const val SERVICE_PACKAGE = "${RPC_PACKAGE}.service"
private const val INTERFACE_PACKAGE = "$RPC_PACKAGE.interfaces"

private val suppressAnnotation = AnnotationSpec
.builder(Suppress::class)
.addMember("%S", "KDocUnresolvedReference") // IntelliJ's seems to get confused with canonical names
.addMember("%S", "RedundantVisibilityModifier") // KotlinPoet is an explicit API generator
.addMember("%S", "unused") // All methods could be used.
.addMember("%S", "FunctionName") // Service messages might be case-sensitive, preserve it.
.build()

private val classAsyncJobSingle = ClassName(
"in.dragonbra.javasteam.types",
"AsyncJobSingle"
)
private val classServiceMethodResponse = ClassName(
"in.dragonbra.javasteam.steam.handlers.steamunifiedmessages.callback",
"ServiceMethodResponse"
)

private val kDocNoResponse = """|No return value.""".trimMargin()
private fun kDocReturns(requestClassName: ClassName, returnClassName: ClassName): String = """
|@param request The request.
|@see [${requestClassName.simpleName}]
|@returns [${returnClassName.canonicalName}]
|@returns [${returnClassName.simpleName}]
""".trimMargin()
}

/**
* Open a .proto file and find all service interfaces.
* Then grab the name of the RPC interface name and everything between the curly braces
* Then loop through all RPC interface methods, destructuring them to name, type, and response and put them in a list.
* Collect the items into a [Service] and pass it off to [buildInterface] and [buildClass]
* Collect the items into a [Service] and pass it off to [buildInterface]
*/
fun parseFile(file: File) {
Regex("""service\s+(\w+)\s*\{([^}]*)}""")
Expand All @@ -66,7 +55,6 @@ class ProtoParser(private val outputDir: File) {
println("[${file.name}] - found \"${service.name}\", which has ${service.methods.size} methods")

buildInterface(file, service)
buildClass(file, service)
}
}
}
Expand Down Expand Up @@ -117,23 +105,25 @@ class ProtoParser(private val outputDir: File) {

// Make a method
val funBuilder = FunSpec
.builder(method.methodName.replaceFirstChar { it.lowercase(Locale.getDefault()) })
.builder(method.methodName)
.addModifiers(KModifier.ABSTRACT)
.addParameter("request", requestClassName)

// Add method kDoc
// Add `AsyncJobSingle<ServiceMethodResponse>` if there is a response
if (method.responseType == "NoResponse") {
funBuilder.addKdoc(kDocNoResponse)
// Add the appropriate return class
val returnPackageName = if (method.responseType == "NoResponse") {
"SteammessagesUnifiedBaseSteamclient"
} else {
val returnClassName = ClassName(
packageName = "in.dragonbra.javasteam.protobufs.steamclient.$protoFileName",
method.responseType
)
val kDoc = kDocReturns(requestClassName, returnClassName)
funBuilder.addKdoc(kDoc)
.returns(classAsyncJobSingle.parameterizedBy(classServiceMethodResponse))
protoFileName
}
val returnClassName = ClassName(
packageName = "in.dragonbra.javasteam.protobufs.steamclient.$returnPackageName",
method.responseType
)

// Add method kDoc
val kDoc = kDocReturns(requestClassName, returnClassName)
funBuilder.addKdoc(kDoc)
.returns(returnClassName)

// Add the function to the interface class.
iBuilder.addFunction(funBuilder.build())
Expand All @@ -145,78 +135,4 @@ class ProtoParser(private val outputDir: File) {
.build()
.writeTo(outputDir)
}

/**
* Build the [Service] to a class with all known RPC methods.
*/
private fun buildClass(file: File, service: Service) {
// Class Builder
val cBuilder = TypeSpec
.classBuilder(service.name)
.addAnnotation(suppressAnnotation)
.addKdoc(RpcGenTask.kDocClass)
.primaryConstructor(
FunSpec.constructorBuilder()
.addParameter(
name = "steamUnifiedMessages",
type = ClassName(
packageName = "in.dragonbra.javasteam.steam.handlers.steamunifiedmessages",
"SteamUnifiedMessages"
)
)
.build()
)
.addSuperclassConstructorParameter("steamUnifiedMessages")
.superclass(
ClassName(
packageName = "in.dragonbra.javasteam.steam.handlers.steamunifiedmessages",
"UnifiedService"
)
)
.addSuperinterface(
ClassName(
packageName = "in.dragonbra.javasteam.rpc.interfaces",
"I${service.name}"
)
)

// Iterate over found 'rpc' methods.
val protoFileName = transformProtoFileName(file.name)
service.methods.forEach { method ->
val requestClassName = ClassName(
packageName = "in.dragonbra.javasteam.protobufs.steamclient.$protoFileName",
method.requestType
)

// Make a method
val funBuilder = FunSpec
.builder(method.methodName.replaceFirstChar { it.lowercase(Locale.getDefault()) })
.addModifiers(KModifier.OVERRIDE)
.addParameter("request", requestClassName)

// Add method kDoc
// Add `AsyncJobSingle<ServiceMethodResponse>` if there is a response
if (method.responseType == "NoResponse") {
funBuilder.addKdoc(kDocNoResponse)
.addStatement("sendNotification(request, %S)", method.methodName)
} else {
val returnClassName = ClassName(
packageName = "in.dragonbra.javasteam.protobufs.steamclient.$protoFileName",
method.responseType
)
val kDoc = kDocReturns(requestClassName, returnClassName)
funBuilder.addKdoc(kDoc)
.returns(classAsyncJobSingle.parameterizedBy(classServiceMethodResponse))
.addStatement("return sendMessage(request, %S)", method.methodName)
}

cBuilder.addFunction(funBuilder.build())
}

// Build everything together and write it
FileSpec.builder(SERVICE_PACKAGE, service.name)
.addType(cBuilder.build())
.build()
.writeTo(outputDir)
}
}
5 changes: 2 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ commons-validator = "1.9.0" # https://mvnrepository.com/artifact/commons-validat
gson = "2.11.0" # https://mvnrepository.com/artifact/com.google.code.gson/gson
jacoco = "0.8.12" # https://www.eclemma.org/jacoco
javaWebSocket = "1.5.7" # https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket
kotlin-coroutines = "1.8.1" # https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
kotlin-coroutines = "1.9.0" # https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-core
okHttp = "5.0.0-alpha.14" # https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp
protobuf = "4.28.0" # https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java
protobuf = "4.28.1" # https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java
protobuf-gradle = "0.9.4" # https://mvnrepository.com/artifact/com.google.protobuf/protobuf-gradle-plugin
publishPlugin = "1.3.0" # https://mvnrepository.com/artifact/io.github.gradle-nexus/publish-plugin
qrCode = "1.0.1" # https://mvnrepository.com/artifact/pro.leaco.qrcode/console-qrcode
Expand All @@ -38,7 +38,6 @@ commons-lang3 = { module = "org.apache.commons:commons-lang3", version.ref = "co
commons-validator = { module = "commons-validator:commons-validator", version.ref = "commons-validator" }
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlin-coroutines" }
kotlin-stdib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8", version.ref = "kotlin" }
okHttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okHttp" }
protobuf-java = { module = "com.google.protobuf:protobuf-java", version.ref = "protobuf" }
protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "protobuf" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import in.dragonbra.javasteam.enums.EUIMode;
import in.dragonbra.javasteam.protobufs.steamclient.SteammessagesFriendmessagesSteamclient.CFriendMessages_IncomingMessage_Notification;
import in.dragonbra.javasteam.protobufs.steamclient.SteammessagesPlayerSteamclient.*;
import in.dragonbra.javasteam.rpc.service.Player;
import in.dragonbra.javasteam.rpc.interfaces.IPlayer;
import in.dragonbra.javasteam.steam.handlers.steamunifiedmessages.SteamUnifiedMessages;
import in.dragonbra.javasteam.steam.handlers.steamunifiedmessages.callback.ServiceMethodNotification;
import in.dragonbra.javasteam.steam.handlers.steamunifiedmessages.callback.ServiceMethodResponse;
Expand Down Expand Up @@ -165,15 +165,17 @@ private void onLoggedOn(LoggedOnCallback callback) {
// at this point, we'd be able to perform actions on Steam

// first, build our request object, these are autogenerated and can normally be found in the in.dragonbra.javasteam.protobufs.steamclient package
CPlayer_GetFavoriteBadge_Request.Builder favoriteBadgeRequest = CPlayer_GetFavoriteBadge_Request.newBuilder();
var favoriteBadgeRequest = CPlayer_GetFavoriteBadge_Request.newBuilder();
favoriteBadgeRequest.setSteamid(steamClient.getSteamID().convertToUInt64());

// now let's send the request, this is done by building a class based off the IPlayer interface.
Player playerService = new Player(steamUnifiedMessages);
favoriteBadge = playerService.getFavoriteBadge(favoriteBadgeRequest.build()).getJobID();
var playerService = steamUnifiedMessages.createService(IPlayer.class);
favoriteBadge = playerService.sendMessage(api ->
api.GetFavoriteBadge(favoriteBadgeRequest.build())
).getJobID();

// second, build our request object, these are autogenerated and can normally be found in the in.dragonbra.javasteam.protobufs.steamclient package
CPlayer_GetGameBadgeLevels_Request.Builder badgeLevelsRequest = CPlayer_GetGameBadgeLevels_Request.newBuilder();
var badgeLevelsRequest = CPlayer_GetGameBadgeLevels_Request.newBuilder();
badgeLevelsRequest.setAppid(440);

// alternatively, the request can be made using SteamUnifiedMessages directly, but then you must build the service request name manually
Expand Down Expand Up @@ -202,7 +204,9 @@ private void onMethodResponse(ServiceMethodResponse callback) {
// for responses: CMyService_Method_Response

if (callback.getJobID().equals(badgeRequest)) {
CPlayer_GetGameBadgeLevels_Response.Builder response = callback.getDeserializedResponse(CPlayer_GetGameBadgeLevels_Response.class);
CPlayer_GetGameBadgeLevels_Response.Builder response = callback.getDeserializedResponse(
CPlayer_GetGameBadgeLevels_Response.class
);

System.out.println("Our player level is " + response.getPlayerLevel());

Expand All @@ -215,7 +219,9 @@ private void onMethodResponse(ServiceMethodResponse callback) {
}

if (callback.getJobID().equals(favoriteBadge)) {
CPlayer_GetFavoriteBadge_Response.Builder response = callback.getDeserializedResponse(CPlayer_GetFavoriteBadge_Response.class);
CPlayer_GetFavoriteBadge_Response.Builder response = callback.getDeserializedResponse(
CPlayer_GetFavoriteBadge_Response.class
);

System.out.println(
"Has favorite badge: " + response.hasHasFavoriteBadge() +
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package `in`.dragonbra.javasteam.steam.authentication

import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesAuthSteamclient
import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesAuthSteamclient.CAuthentication_AccessToken_GenerateForApp_Response
import `in`.dragonbra.javasteam.steam.handlers.steamuser.LogOnDetails

/**
* Represents access token generation result.
*/
class AccessTokenGenerateResult(
response: SteammessagesAuthSteamclient.CAuthentication_AccessToken_GenerateForApp_Response.Builder,
) {
class AccessTokenGenerateResult(response: CAuthentication_AccessToken_GenerateForApp_Response) {

/**
* New refresh token.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import `in`.dragonbra.javasteam.protobufs.steamclient.SteammessagesAuthSteamclie
/**
* Represents authentication poll result.
*/
class AuthPollResult(response: CAuthentication_PollAuthSessionStatus_Response.Builder) {
class AuthPollResult(response: CAuthentication_PollAuthSessionStatus_Response) {

/**
* Account name of authenticating account.
Expand Down
Loading
Loading