This is an automated email from the ASF dual-hosted git repository.

xuetaoli pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/dubbo-go-samples.git


The following commit(s) were added to refs/heads/main by this push:
     new 863a0881 feat: migrate old game sample (#970)
863a0881 is described below

commit 863a0881aa890e6dcc3ebf3400a6d8dafeb7c7f5
Author: zbchi <[email protected]>
AuthorDate: Wed Nov 26 17:01:18 2025 +0800

    feat: migrate old game sample (#970)
    
    * feat: migrate old game sample
    
    * migrate integrate test
    
    * fix docs mistake
---
 README.md                                          |   2 +-
 README_CN.md                                       |   2 +-
 compatibility/game/go-server-game/cmd/server.go    |  79 -----
 compatibility/game/go-server-game/conf/dubbogo.yml |  54 ---
 compatibility/game/go-server-game/pkg/consumer.go  |  24 --
 compatibility/game/go-server-game/pkg/provider.go  | 127 -------
 compatibility/game/go-server-gate/conf/dubbogo.yml |  56 ---
 compatibility/game/go-server-gate/pkg/consumer.go  |  43 ---
 compatibility/game/pkg/consumer/game/basketball.go |  36 --
 compatibility/game/pkg/consumer/gate/basketball.go |  34 --
 compatibility/game/pkg/pojo/info.go                |  27 --
 compatibility/game/website/css/style.css           |  54 ---
 compatibility/game/website/index.html              |  56 ---
 compatibility/game/website/js/api.js               |  64 ----
 compatibility/game/website/js/index.js             | 130 -------
 game/README.md                                     |  72 ++++
 game/README_zh.md                                  |  75 ++++
 .../game/go-server/cmd/main.go                     |  45 ++-
 .../main_test.go => game/game/pkg/consumer.go      |  38 +-
 game/game/pkg/provider.go                          | 173 +++++++++
 .../server.go => game/gate/go-server/cmd/main.go   | 117 +++++-
 game/gate/pkg/consumer.go                          |  98 +++++
 .../go-server-gate => game/gate}/pkg/provider.go   |  20 +-
 game/proto/game/game.pb.go                         | 395 +++++++++++++++++++++
 .../main_test.go => game/proto/game/game.proto     |  47 +--
 game/proto/game/game.triple.go                     | 176 +++++++++
 game/proto/gate/gate.pb.go                         | 262 ++++++++++++++
 .../pojo/result.go => game/proto/gate/gate.proto   |  26 +-
 game/proto/gate/gate.triple.go                     | 122 +++++++
 game/website/css/style.css                         | 238 +++++++++++++
 {compatibility/game => game}/website/img/bac.png   | Bin
 game/website/img/ball.svg                          |  11 +
 game/website/img/gate.svg                          |   5 +
 game/website/img/people.svg                        |   7 +
 game/website/index.html                            |  76 ++++
 game/website/js/api.js                             |  77 ++++
 game/website/js/index.js                           | 228 ++++++++++++
 .../game/game/tests/integration/main_test.go       | 152 ++++++++
 .../game}/tests/integration/provider_test.go       |  43 ++-
 .../gate}/tests/integration/main_test.go           |  27 +-
 .../gate}/tests/integration/provider_test.go       |  13 +-
 start_integrate_test.sh                            |   4 +-
 42 files changed, 2432 insertions(+), 903 deletions(-)

diff --git a/README.md b/README.md
index 6c297ec2..8e8bf766 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ A collection of runnable Dubbo-go examples covering 
configuration, registries, o
 * `healthcheck`: Service health check example.
 * `helloworld`: Basic “Hello World” example for Dubbo-go, also includes 
Go–Java interoperability.
 * `direct`: Triple point-to-point invocation sample without a registry.
+* `game`: Game service example.
 * `integrate_test`: Integration test cases for Dubbo-go samples.
 * `java_interop`: Demonstrates interoperability between Java and Go Dubbo 
implementations.
 * `llm`: Example of integrating Large Language Models (LLMs) with Dubbo-go.
@@ -49,7 +50,6 @@ A collection of runnable Dubbo-go examples covering 
configuration, registries, o
 * `compatibility/config-api`: Shows how to use Dubbo-go via APIs without 
configuration files.
 * `compatibility/configcenter`: Usage of different config centers, including 
Zookeeper, Nacos, and Apollo.
 * `compatibility/filter`: Examples of different filters, including 
`custom_filter` and `tpslimit`.
-* `compatibility/game`: Game service example.
 * `compatibility/generic`: Generic invocation example.
 * `compatibility/mesh`: Proxy-based service mesh example showing how to deploy 
Dubbo-go services with Envoy on Kubernetes.
 * `compatibility/proxyless`: Proxyless service mesh example for deploying 
Dubbo-go services on Kubernetes.
diff --git a/README_CN.md b/README_CN.md
index ac91dc38..2638fcfc 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -21,6 +21,7 @@
 * `healthcheck`:服务健康检查示例。
 * `helloworld`:Dubbo-go 最基础的 “Hello World” 示例,同时包含 Go 与 Java 的互操作示例。
 * `direct`:不依赖注册中心的 Triple 点对点调用示例。
+* `game`:游戏服务示例。
 * `integrate_test`:Dubbo-go 示例的集成测试用例。
 * `java_interop`:展示 Java 与 Go Dubbo 实现之间的互操作能力。
 * `llm`:将大模型(LLM)集成到 Dubbo-go 中的示例。
@@ -49,7 +50,6 @@
 * `compatibility/config-api`:演示如何在不使用配置文件的情况下,通过 API 使用 Dubbo-go。
 * `compatibility/configcenter`:不同配置中心的使用示例,包括 Zookeeper、Nacos 和 Apollo。
 * `compatibility/filter`:多种 Filter 示例,包括 `custom_filter` 和 `tpslimit`。
-* `compatibility/game`:游戏服务示例。
 * `compatibility/generic`:泛化调用示例。
 * `compatibility/mesh`:基于代理的服务网格示例,展示如何在 Kubernetes 上结合 Envoy 部署 Dubbo-go 服务。
 * `compatibility/proxyless`:无 Sidecar 的服务网格示例,展示在 Kubernetes 上的部署方式。
diff --git a/compatibility/game/go-server-game/cmd/server.go 
b/compatibility/game/go-server-game/cmd/server.go
deleted file mode 100644
index 63f56776..00000000
--- a/compatibility/game/go-server-game/cmd/server.go
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package main
-
-import (
-       "os"
-       "os/signal"
-       "syscall"
-       "time"
-)
-
-import (
-       "dubbo.apache.org/dubbo-go/v3/config"
-       _ "dubbo.apache.org/dubbo-go/v3/imports"
-
-       hessian "github.com/apache/dubbo-go-hessian2"
-
-       "github.com/dubbogo/gost/log/logger"
-)
-
-import (
-       
"github.com/apache/dubbo-go-samples/compatibility/game/go-server-game/pkg"
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
-)
-
-func init() {
-       config.SetProviderService(new(pkg.BasketballService))
-
-       config.SetConsumerService(pkg.GateBasketball)
-
-       hessian.RegisterPOJO(&pojo.Result{})
-}
-
-func main() {
-       err := config.Load()
-       if err != nil {
-               panic(err)
-       }
-
-       initSignal()
-}
-
-func initSignal() {
-       signals := make(chan os.Signal, 1)
-
-       signal.Notify(signals, os.Interrupt, syscall.SIGHUP, syscall.SIGQUIT, 
syscall.SIGTERM)
-       for {
-               sig := <-signals
-               logger.Infof("get signal %#s", sig.String())
-               switch sig {
-               case syscall.SIGHUP:
-                       logger.Infof("app need reload")
-               default:
-                       time.AfterFunc(time.Duration(time.Second*5), func() {
-                               logger.Warnf("app exit now by force...")
-                               os.Exit(1)
-                       })
-
-                       // The program exits normally or timeout forcibly exits.
-                       logger.Warnf("app exit now...")
-                       return
-               }
-       }
-}
diff --git a/compatibility/game/go-server-game/conf/dubbogo.yml 
b/compatibility/game/go-server-game/conf/dubbogo.yml
deleted file mode 100644
index 6441204b..00000000
--- a/compatibility/game/go-server-game/conf/dubbogo.yml
+++ /dev/null
@@ -1,54 +0,0 @@
-dubbo:
-  registries:
-    demoZK:
-      protocol: zookeeper
-      timeout: 3s
-      address: 127.0.0.1:2181
-  consumer:
-    registry-ids:
-      - demoZK
-    references:
-      gateConsumer.basketballService:
-        protocol: dubbo
-        interface: org.apache.dubbo.gate.basketballService
-        cluster: "failover"
-        methods:
-          - name: "Send"
-            retries: 0
-  provider:
-    registry-ids:
-      - demoZK
-    services:
-      gameProvider.basketballService:
-        protocol: dubbo
-        interface: org.apache.dubbo.game.basketballService # must be 
compatible with grpc or dubbo-java
-        loadbalance: "random"
-        warmup: "100"
-        cluster: "failover"
-        methods:
-          - name: "Online"
-            retries: 0
-          - name: "Offline"
-            retries: 0
-          - name: "Message"
-            retries: 0
-  protocols:
-    dubbo:
-      name: dubbo
-      port: 20000
-      # 在 params 中定义当前使用的协议特有的网络配置
-      # 如该 sample 配置的是 dubbo 协议(底层使用 getty 通信库)
-      params:
-        session-number: 700
-        session-timeout: 180s
-        compress-encoding: false
-        tcp-no-delay: true
-        tcp-keep-alive: true
-        keep-alive-period: 120s
-        tcp-r-buf-size: 262144
-        tcp-w-buf-size: 65536
-        tcp-read-timeout: 10s
-        tcp-write-timeout: 5s
-        wait-timeout: 1s
-        max-msg-len: 1024000
-        session-name: server
\ No newline at end of file
diff --git a/compatibility/game/go-server-game/pkg/consumer.go 
b/compatibility/game/go-server-game/pkg/consumer.go
deleted file mode 100644
index 00888d07..00000000
--- a/compatibility/game/go-server-game/pkg/consumer.go
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package pkg
-
-import (
-       
"github.com/apache/dubbo-go-samples/compatibility/game/pkg/consumer/gate"
-)
-
-var GateBasketball = gate.BasketballService{}
diff --git a/compatibility/game/go-server-game/pkg/provider.go 
b/compatibility/game/go-server-game/pkg/provider.go
deleted file mode 100644
index 64ba17dd..00000000
--- a/compatibility/game/go-server-game/pkg/provider.go
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package pkg
-
-import (
-       "context"
-       "fmt"
-       "strconv"
-)
-
-import (
-       "github.com/dubbogo/gost/log/logger"
-)
-
-import (
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
-)
-
-type BasketballService struct{}
-
-var userMap = make(map[string]*pojo.Info)
-
-func (p *BasketballService) Login(ctx context.Context, uid string) 
(*pojo.Result, error) {
-       logger.Infof("message: %#v", uid)
-       var (
-               info *pojo.Info
-               ok   bool
-       )
-
-       // auto reply the same message
-       rsp, err := GateBasketball.Send(context.TODO(), uid, "")
-       if err != nil {
-               logger.Errorf("send fail: %#s", err.Error())
-               return &pojo.Result{Code: 1, Msg: err.Error()}, err
-       }
-
-       fmt.Println("receive data from gate:", rsp)
-
-       if info, ok = userMap[uid]; !ok {
-               info = &pojo.Info{}
-               info.Name = uid
-               userMap[uid] = info
-       }
-       return &pojo.Result{Code: 0, Msg: info.Name + ", your score is " + 
strconv.Itoa(info.Score), Data: map[string]interface{}{"to": uid, "score": 
info.Score}}, nil
-}
-
-func (p *BasketballService) Score(ctx context.Context, uid, score string) 
(*pojo.Result, error) {
-       logger.Infof("message: %#v, %#v", uid, score)
-       var (
-               info = &pojo.Info{}
-               ok   bool
-       )
-
-       // auto reply the same message
-       rsp, err := GateBasketball.Send(context.TODO(), uid, score)
-       if err != nil {
-               logger.Errorf("send fail: %#s", err.Error())
-               return &pojo.Result{Code: 1, Msg: err.Error()}, err
-       }
-
-       fmt.Println("receive data from gate:", rsp)
-
-       if info, ok = userMap[uid]; !ok {
-               info = &pojo.Info{
-                       Name: uid,
-               }
-               userMap[uid] = info
-               logger.Error("user data not found")
-               return &pojo.Result{Code: 1, Msg: "user data not found", Data: 
map[string]interface{}{}}, nil
-       }
-       intSource, err := strconv.Atoi(score)
-       if err != nil {
-               logger.Error(err.Error())
-       }
-       info.Score += intSource
-
-       return &pojo.Result{Code: 0, Msg: "进球成功", Data: 
map[string]interface{}{"to": uid, "score": info.Score}}, nil
-}
-
-func (p *BasketballService) Rank(ctx context.Context, uid string) 
(*pojo.Result, error) {
-       var (
-               rank = 1
-               info *pojo.Info
-               ok   bool
-       )
-
-       // auto reply the same message
-       rsp, err := GateBasketball.Send(context.TODO(), uid, "")
-       if err != nil {
-               logger.Errorf("send fail: %#s", err.Error())
-               return &pojo.Result{Code: 1, Msg: err.Error()}, err
-       }
-
-       fmt.Println("receive data from gate:", rsp)
-
-       if info, ok = userMap[uid]; !ok {
-               logger.Error("no user found")
-               return &pojo.Result{Code: 1, Msg: "no user found", Data: 
map[string]interface{}{"to": uid, "rank": rank}}, nil
-       }
-
-       for _, v := range userMap {
-               if v.Score > info.Score {
-                       rank++
-               }
-       }
-
-       return &pojo.Result{Code: 0, Msg: "success", Data: 
map[string]interface{}{"to": uid, "rank": rank}}, nil
-}
-
-func (p *BasketballService) Reference() string {
-       return "gameProvider.basketballService"
-}
diff --git a/compatibility/game/go-server-gate/conf/dubbogo.yml 
b/compatibility/game/go-server-gate/conf/dubbogo.yml
deleted file mode 100644
index d2bb08fd..00000000
--- a/compatibility/game/go-server-gate/conf/dubbogo.yml
+++ /dev/null
@@ -1,56 +0,0 @@
-dubbo:
-  registries:
-    demoZK:
-      protocol: zookeeper
-      timeout: 3s
-      address: 127.0.0.1:2181
-  consumer:
-    registry-ids:
-      - demoZK
-    references:
-      gameConsumer.basketballService:
-        protocol: dubbo
-        interface: org.apache.dubbo.game.basketballService
-        cluster: "failover"
-        registry-ids:
-          - demoZK
-        methods:
-          - name: "Online"
-            retries: 0
-          - name: "Offline"
-            retries: 0
-          - name: "Message"
-            retries: 0
-  provider:
-    registry-ids:
-      - demoZK
-    services:
-      gateProvider.basketballService:
-        protocol: dubbo
-        interface: org.apache.dubbo.gate.basketballService # must be 
compatible with grpc or dubbo-java
-        loadbalance: "random"
-        warmup: "100"
-        cluster: "failover"
-        methods:
-          - name: "Send"
-            retries: 0
-  protocols:
-    dubbo:
-      name: dubbo
-      port: 20001
-      # 在 params 中定义当前使用的协议特有的网络配置
-      # 如该 sample 配置的是 dubbo 协议(底层使用 getty 通信库)
-      params:
-        session-number: 700
-        session-timeout: 180s
-        compress-encoding: false
-        tcp-no-delay: true
-        tcp-keep-alive: true
-        keep-alive-period: 120s
-        tcp-r-buf-size: 262144
-        tcp-w-buf-size: 65536
-        tcp-read-timeout: 10s
-        tcp-write-timeout: 5s
-        wait-timeout: 1s
-        max-msg-len: 1024000
-        session-name: server
\ No newline at end of file
diff --git a/compatibility/game/go-server-gate/pkg/consumer.go 
b/compatibility/game/go-server-gate/pkg/consumer.go
deleted file mode 100644
index 74c635bd..00000000
--- a/compatibility/game/go-server-gate/pkg/consumer.go
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package pkg
-
-import (
-       "context"
-)
-
-import (
-       
"github.com/apache/dubbo-go-samples/compatibility/game/pkg/consumer/game"
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
-)
-
-var GameBasketball = game.BasketballService{}
-
-// just easy for demo test
-
-func Login(ctx context.Context, data string) (*pojo.Result, error) {
-       return GameBasketball.Login(ctx, data)
-}
-
-func Score(ctx context.Context, uid, score string) (*pojo.Result, error) {
-       return GameBasketball.Score(ctx, uid, score)
-}
-
-func Rank(ctx context.Context, uid string) (*pojo.Result, error) {
-       return GameBasketball.Rank(ctx, uid)
-}
diff --git a/compatibility/game/pkg/consumer/game/basketball.go 
b/compatibility/game/pkg/consumer/game/basketball.go
deleted file mode 100644
index 4e733e4b..00000000
--- a/compatibility/game/pkg/consumer/game/basketball.go
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package game
-
-import (
-       "context"
-)
-
-import (
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
-)
-
-type BasketballService struct {
-       Login func(ctx context.Context, uid string) (*pojo.Result, error)
-       Score func(ctx context.Context, uid, score string) (*pojo.Result, error)
-       Rank  func(ctx context.Context, uid string) (*pojo.Result, error)
-}
-
-func (p *BasketballService) Reference() string {
-       return "gameConsumer.basketballService"
-}
diff --git a/compatibility/game/pkg/consumer/gate/basketball.go 
b/compatibility/game/pkg/consumer/gate/basketball.go
deleted file mode 100644
index d342cf63..00000000
--- a/compatibility/game/pkg/consumer/gate/basketball.go
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package gate
-
-import (
-       "context"
-)
-
-import (
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
-)
-
-type BasketballService struct {
-       Send func(ctx context.Context, uid string, data string) (*pojo.Result, 
error)
-}
-
-func (p *BasketballService) Reference() string {
-       return "gateConsumer.basketballService"
-}
diff --git a/compatibility/game/pkg/pojo/info.go 
b/compatibility/game/pkg/pojo/info.go
deleted file mode 100644
index e3a03160..00000000
--- a/compatibility/game/pkg/pojo/info.go
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package pojo
-
-type Info struct {
-       Name  string `json:"name"`
-       Score int    `json:"score"`
-}
-
-func (m *Info) JavaClassName() string {
-       return "org.apache.dubbo.pojo.Info"
-}
diff --git a/compatibility/game/website/css/style.css 
b/compatibility/game/website/css/style.css
deleted file mode 100644
index 66dffa0d..00000000
--- a/compatibility/game/website/css/style.css
+++ /dev/null
@@ -1,54 +0,0 @@
-*{
-    margin: 0;
-    padding: 0;
-}
-#myProgress {
-    width: 100%;
-    background-color: #ddd;
-}
-#myBar {
-    width: 0%;
-    height: 30px;
-    background-color: #4CAF50;
-    text-align: center;
-    line-height: 30px;
-    color: white;
-}
-.background{
-    background-image: url('bac.png');
-    position: absolute;
-    background-size: 100% 100%;
-    background-repeat: no-repeat;
-    width: 100%;
-    height:100%;
-}
-.gate{
-    width:8rem;
-    height: 4rem;
-    top: 5%;
-    left: 50%;
-    margin-left: -4rem;
-    position: absolute;
-}
-.ball{
-    width: 3rem;
-    height: 3rem;
-    bottom: 0;
-    left: 50%;
-    margin-left: -2.5rem;
-    position: absolute;
-}
-.people{
-    width: 5rem;
-    height: 10rem;
-    bottom: 0;
-    left: 50%;
-    margin-left: -2.5rem;
-    position: absolute;
-}
-.info{
-    position: absolute;
-    top: 10px;
-    left: 10px;
-    color: white;
-}
\ No newline at end of file
diff --git a/compatibility/game/website/index.html 
b/compatibility/game/website/index.html
deleted file mode 100644
index a3554dbf..00000000
--- a/compatibility/game/website/index.html
+++ /dev/null
@@ -1,56 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-
-<head>
-    <meta charset="UTF-8">
-    <meta http-equiv="X-UA-Compatible" content="IE=edge">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>FootBall Game</title>
-    <script 
src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js";></script>
-    <!-- import Vue before Element -->
-    <script src="https://unpkg.com/vue/dist/vue.js";></script>
-    <!-- 引入样式 -->
-    <link rel="stylesheet" 
href="https://unpkg.com/element-ui/lib/theme-chalk/index.css";>
-    <link rel="stylesheet" href="css/style.css">
-    <!-- 引入组件库 -->
-    <script src="https://unpkg.com/element-ui/lib/index.js";></script>
-
-</head>
-
-<body>
-    <div id="app" class="background">
-        <div id="myProgress">
-            <div id="myBar">0%</div>
-        </div>
-        <div class="info" v-if="!dialogVisible">
-            用户:{{info.name}}
-            <br>
-            分数:{{info.score}}
-            <br>
-            排名:{{info.rank}}
-        </div>
-        <svg id="gate" class="gate" t="1620527545136" class="icon" viewBox="0 
0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"; p-id="2966" 
width="400" height="400"><path d="M861.414041 192.806237 179.968861 
192.806237c-51.828412 0-93.985558 42.164309-93.985558 93.985558l0 514.915399 
61.975517 0 0-0.276293 0.796132 0.650823 74.704422-89.905636 597.571934 0 
72.39482 87.839581 0 1.691525 62.007239 0L955.433368 286.791795C955.432345 
234.970546 913.228127 192.806237 861.414041 19 [...]
-        <svg id="ball" class="ball" t="1620527208144" class="icon" viewBox="0 
0 1365 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"; p-id="1801" 
width="400" height="400"><path d="M647.937 1023.573c-40.96 
0-82.133-5.12-122.24-14.933-66.773-16.427-128.427-45.44-183.04-86.4-52.693-39.467-96.853-88.107-131.2-144.853-34.347-56.747-56.96-118.4-67.2-183.467-10.88-67.413-7.893-135.467
 8.533-202.24 14.08-56.747 37.334-110.293 69.12-158.72 30.934-46.72 
69.12-88.107 113.707-122.453 44.16-34. [...]
-        <svg class="people" @click="clickPeople" t="1620527325090" 
class="icon" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; p-id="2618" width="400" height="400"><path 
d="M563.240423 102.320551m-102.320551 0a102.320551 102.320551 0 1 0 204.641102 
0 102.320551 102.320551 0 1 0-204.641102 0Z" p-id="2619" 
fill="#e6e6e6"></path><path d="M322.895467 827.809372c-8.907907 
20.054828-155.767992 195.805422-155.767992 
195.805421h122.447605s106.292996-119.58263 113.924906 [...]
-        <el-dialog
-        title="输入姓名"
-        :visible="dialogVisible"
-        width="30%"
-        :show-close="false"
-        >
-        <el-form v-model="info">
-            <el-form-item label="姓名">
-                <el-input v-model="info.name"></el-input>
-            </el-form-item>
-        </el-form>
-        <span slot="footer" class="dialog-footer">
-            <el-button type="primary" @click="submitInfo">确 定</el-button>
-        </span>
-        </el-dialog>
-    </div>
-    <script src="js/api.js"></script>
-    <script src="js/index.js"></script>
-    
-</body>
-
-</html>
\ No newline at end of file
diff --git a/compatibility/game/website/js/api.js 
b/compatibility/game/website/js/api.js
deleted file mode 100644
index 32a23588..00000000
--- a/compatibility/game/website/js/api.js
+++ /dev/null
@@ -1,64 +0,0 @@
-var baseURL = 'http://127.0.0.1:8089/'
-
-function login(data) {
-    return new Promise(function(resolve, reject) {
-        $.ajax({
-            type: "get",
-            url: baseURL + 'login',
-            data:data,
-            dataType: "json", //指定服务器返回的数据类型
-            success: function (response) {
-                if (response.code != 0) {
-                    reject(response.msg)
-                } else {
-                    resolve(response)
-                }
-            },
-            error:function(err){
-                reject(err)
-            }
-        });
-    })
-}
-
-function score(data) {
-    return new Promise(function(resolve, reject) {
-        $.ajax({
-            type: "post",
-            url: baseURL + 'score',
-            data:data,
-            dataType: "json", //指定服务器返回的数据类型
-            success: function (response) {
-                if (response.code != 0) {
-                    reject(response.msg)
-                } else {
-                    resolve(response)
-                }
-            },
-            error:function(err){
-                reject(err)
-            }
-        })
-    })
-}
-
-function rank(data) {
-    return new Promise(function(resolve, reject) {
-        $.ajax({
-            type: "get",
-            url: baseURL + 'rank',
-            data:data,
-            dataType: "json", //指定服务器返回的数据类型
-            success: function (response) {
-                if (response.code != 0) {
-                    reject(response.msg)
-                } else {
-                    resolve(response)
-                }
-            },
-            error:function(err){
-                reject(err)
-            }
-        })
-    })
-}
\ No newline at end of file
diff --git a/compatibility/game/website/js/index.js 
b/compatibility/game/website/js/index.js
deleted file mode 100644
index 7e9be357..00000000
--- a/compatibility/game/website/js/index.js
+++ /dev/null
@@ -1,130 +0,0 @@
-new Vue({
-    el: '#app',
-    data: function () {
-        return { 
-            visible: false,
-            dialogVisible:true,
-            info:{
-                name:'',
-                score:0,
-                rank:0
-            },
-            isMeet:false
-        }
-    },
-    computed:{
-        clientHeight:function(){
-            return document.documentElement.clientWidth
-        },
-        gateHeight:function(){
-            var self = document.getElementById("gate");
-            return self.getBoundingClientRect().top + 
document.documentElement.scrollTop
-        },
-        geteLeft:function(){
-            var self = document.getElementById("gate");
-            return self.getBoundingClientRect().left
-        }
-    },
-    methods: {
-        getRank:function(){
-            rank({name: this.info.name}).then(Response => {
-                this.info.rank = Response.data.rank
-            }).catch(e => {
-                this.$message({
-                    message: "获取排名失败" + e,
-                    type:"danger"
-                })
-            })
-        },
-        submitInfo:function(){
-            this.dialogVisible = false
-            login({name: this.info.name}).then(Response => {
-                this.info.name = Response.data.to
-                this.$message({
-                    message:"欢迎" + Response.msg
-                })
-                this.getRank()
-            }).catch(e => {
-                this.$message({
-                    message: "登陆失败" + e,
-                    type:"danger"
-                })
-            })
-        },
-        moveBall:function(){
-            var elem = document.getElementsByClassName("ball")
-            var gate = document.getElementById("gate")
-            var id = setInterval(frame, 20)
-            var tempHeight = 0
-            var that = this
-            function frame() {
-                var space = elem[0].getBoundingClientRect().left - 
gate.getBoundingClientRect().left
-                var hight = elem[0].getBoundingClientRect().top - 
gate.getBoundingClientRect().top
-                if (!that.isMeet && (space <60 && space > -20 && hight < 20 && 
hight > -20)) {
-                    that.isMeet = true
-                    score(JSON.stringify({name:that.info.name, 
score:1})).then(Response => {
-                        that.info.score = Response.data.score
-                        that.$message({
-                            message:"进球成功, 总分数为:" + Response.data.score,
-                            type:"success"
-                        })
-                        that.getRank()
-                    }).catch( e => {
-                        that.$message({
-                            message: "分数统计失败" + e,
-                            type:"danger"
-                        })
-                    })
-                }
-                if (tempHeight >= that.clientHeight) {
-                    elem[0].style.bottom = 0 + ''
-                    clearInterval(id)
-                } else{
-                    tempHeight += 20
-                    elem[0].style.bottom = tempHeight + ''
-                }
-            }
-        },
-        move: function () {
-            var elem = document.getElementById("myBar")
-            var width = 0
-            var id = setInterval(frame, 20)
-            function frame() {
-                if (width >= 100) {
-                    clearInterval(id)
-                    elem.remove()
-                } else {
-                    width++
-                    elem.style.width = width + '%'
-                    elem.innerHTML = width * 1 + '%'
-                }
-            }
-            
-        },
-        moveGate:function(){
-            var elem = document.getElementById("gate")
-            var id = setInterval(frame, 20)
-            var model = false
-            var tempValue = 0
-            var increase = 1
-            function frame() {
-                if (tempValue > 100 || tempValue < -100) {
-                    increase = -increase
-                }
-                tempValue += increase
-                elem.style.marginLeft = tempValue + 'px'
-            }
-        },
-        clickPeople(){
-            this.isMeet = false
-            this.moveBall()
-        }
-    },
-    mounted(){
-        this.move()
-        this.moveGate()
-    },
-    created() {
-        
-    }
-})
\ No newline at end of file
diff --git a/game/README.md b/game/README.md
new file mode 100644
index 00000000..e066050d
--- /dev/null
+++ b/game/README.md
@@ -0,0 +1,72 @@
+# Game Example for dubbo-go
+
+English | [中文](README_zh.md)
+
+This example demonstrates a football game application using dubbo-go as an RPC 
framework. It consists of two services: a game service that handles game logic 
(login, scoring, ranking) and a gate service that provides HTTP endpoints for 
the web frontend and acts as a gateway between the frontend and game service.
+
+## Architecture
+
+- **game**: Game service that handles game logic (Login, Score, Rank)
+- **gate**: Gate service that provides HTTP API for web frontend and RPC 
service for game service
+- **website**: Web frontend for the football game
+- **proto**: Protocol buffer definitions for game and gate services
+
+## Contents
+
+- `game/go-server/cmd/main.go` - Game service server implementation
+- `game/pkg/provider.go` - Game service handler implementation
+- `gate/go-server/cmd/main.go` - Gate service server with HTTP and RPC
+- `gate/pkg/provider.go` - Gate service handler implementation
+- `gate/pkg/consumer.go` - Game service client for gate service
+- `proto/` - Protocol buffer definitions and generated code
+- `website/` - Web frontend files
+
+## How to run
+
+### Run Game Server
+
+Start the game service server (listens on port 20000):
+
+```shell
+go run ./game/go-server/cmd/main.go
+```
+
+### Run Gate Server
+
+Start the gate service server (listens on port 20001 for RPC and 8089 for 
HTTP):
+
+```shell
+go run ./gate/go-server/cmd/main.go
+```
+
+### Access Web Frontend
+
+Open `http://127.0.0.1:8089/` in your browser to access the game frontend. The 
frontend will communicate with the gate service HTTP API.
+
+## Service Communication
+
+1. **Frontend → Gate Service**: HTTP requests to `/login`, `/score`, `/rank`
+2. **Gate Service → Game Service**: RPC calls using Triple protocol
+3. **Game Service → Gate Service**: RPC calls for gate operations
+
+## Testing
+
+You can test the services using curl:
+
+```shell
+# Test login
+curl "http://127.0.0.1:8089/login?name=player1";
+
+# Test score
+curl -X POST http://127.0.0.1:8089/score \
+  -H "Content-Type: application/json" \
+  -d '{"name":"player1","score":1}'
+
+# Test rank
+curl "http://127.0.0.1:8089/rank?name=player1";
+```
+
+## Notes
+
+- Make sure both game server and gate server are running before accessing the 
web frontend
+- The game server must be started before the gate server, as the gate server 
needs to connect to the game service
diff --git a/game/README_zh.md b/game/README_zh.md
new file mode 100644
index 00000000..13a55406
--- /dev/null
+++ b/game/README_zh.md
@@ -0,0 +1,75 @@
+# dubbo-go 游戏示例
+
+[English](README.md) | 中文
+
+本示例演示了一个使用 dubbo-go 作为 RPC 框架的足球游戏应用。它包含两个服务:处理游戏逻辑的游戏服务(登录、得分、排名)和提供 Web 前端 
HTTP 接口的网关服务,作为前端和游戏服务之间的网关。
+
+## 架构
+
+- **game**: 处理游戏逻辑的游戏服务(Login、Score、Rank)
+- **gate**: 为 Web 前端提供 HTTP API 并为游戏服务提供 RPC 服务的网关服务
+- **website**: 足球游戏的 Web 前端
+- **proto**: 游戏和网关服务的 Protocol Buffer 定义
+
+## 目录结构
+
+- `game/go-server/cmd/main.go` - 游戏服务服务器实现
+- `game/pkg/provider.go` - 游戏服务处理器实现
+- `gate/go-server/cmd/main.go` - 带 HTTP 和 RPC 的网关服务服务器
+- `gate/pkg/provider.go` - 网关服务处理器实现
+- `gate/pkg/consumer.go` - 网关服务调用游戏服务的客户端
+- `proto/` - Protocol Buffer 定义和生成的代码
+- `website/` - Web 前端文件
+
+## 运行方法
+
+### 启动游戏服务
+
+启动游戏服务服务器(监听端口 20000):
+
+```shell
+go run ./game/go-server/cmd/main.go
+```
+
+### 启动网关服务
+
+启动网关服务服务器(监听端口 20001 用于 RPC,8089 用于 HTTP):
+
+```shell
+go run ./gate/go-server/cmd/main.go
+```
+
+### 访问 Web 前端
+
+在浏览器中打开 `http://127.0.0.1:8089/` 访问游戏前端页面。前端将与网关服务的 HTTP API 通信。
+
+## 服务通信
+
+1. **前端 → 网关服务**: 对 `/login`、`/score`、`/rank` 的 HTTP 请求
+2. **网关服务 → 游戏服务**: 使用 Triple 协议的 RPC 调用
+3. **游戏服务 → 网关服务**: 用于网关操作的 RPC 调用
+
+## 测试
+
+您可以使用 curl 测试服务:
+
+```shell
+# 测试登录
+curl "http://127.0.0.1:8089/login?name=player1";
+
+# 测试得分
+curl -X POST http://127.0.0.1:8089/score \
+  -H "Content-Type: application/json" \
+  -d '{"name":"player1","score":1}'
+
+# 测试排名
+curl "http://127.0.0.1:8089/rank?name=player1";
+```
+
+## 注意事项
+
+- 在访问 Web 前端之前,请确保游戏服务器和网关服务器都在运行
+- 游戏服务器必须在网关服务器之前启动,因为网关服务器需要连接到游戏服务
+
+[version3]: https://protobuf.dev/programming-guides/proto3/
+[Protocol Buffer Compiler Installation]: 
https://dubbo-next.staged.apache.org/zh-cn/overview/reference/protoc-installation/
diff --git 
a/integrate_test/compatibility/game/go-server-game/tests/integration/main_test.go
 b/game/game/go-server/cmd/main.go
similarity index 51%
copy from 
integrate_test/compatibility/game/go-server-game/tests/integration/main_test.go
copy to game/game/go-server/cmd/main.go
index 14c98300..db5e0a95 100644
--- 
a/integrate_test/compatibility/game/go-server-game/tests/integration/main_test.go
+++ b/game/game/go-server/cmd/main.go
@@ -15,35 +15,44 @@
  * limitations under the License.
  */
 
-package integration
+package main
 
 import (
-       "os"
-       "testing"
-)
-
-import (
-       "dubbo.apache.org/dubbo-go/v3/config"
        _ "dubbo.apache.org/dubbo-go/v3/imports"
+       "dubbo.apache.org/dubbo-go/v3/protocol"
+       "dubbo.apache.org/dubbo-go/v3/server"
 
-       hessian "github.com/apache/dubbo-go-hessian2"
+       "github.com/dubbogo/gost/log/logger"
 )
 
 import (
-       
"github.com/apache/dubbo-go-samples/compatibility/game/go-server-game/pkg"
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
+       "github.com/apache/dubbo-go-samples/game/game/pkg"
+       game "github.com/apache/dubbo-go-samples/game/proto/game"
 )
 
-var gameProvider = pkg.BasketballService{}
-
-func TestMain(m *testing.M) {
-       config.SetConsumerService(gameProvider)
+func main() {
+       srv, err := server.NewServer(
+               server.WithServerProtocol(
+                       protocol.WithPort(20000),
+                       protocol.WithTriple(),
+               ),
+       )
+       if err != nil {
+               logger.Fatalf("failed to create server: %v", err)
+       }
 
-       hessian.RegisterPOJO(&pojo.Result{})
-       err := config.Load()
+       // Initialize gate client
+       cli, err := pkg.InitGateClient()
        if err != nil {
-               panic(err)
+               logger.Fatalf("failed to create gate client: %v", err)
+       }
+       pkg.SetGateClient(cli)
+
+       if err := game.RegisterGameServiceHandler(srv, 
&pkg.GameServiceHandler{}); err != nil {
+               logger.Fatalf("failed to register game service handler: %v", 
err)
        }
 
-       os.Exit(m.Run())
+       if err := srv.Serve(); err != nil {
+               logger.Fatalf("failed to serve: %v", err)
+       }
 }
diff --git 
a/integrate_test/compatibility/game/go-server-gate/tests/integration/main_test.go
 b/game/game/pkg/consumer.go
similarity index 60%
rename from 
integrate_test/compatibility/game/go-server-gate/tests/integration/main_test.go
rename to game/game/pkg/consumer.go
index 6241ab08..0968ace2 100644
--- 
a/integrate_test/compatibility/game/go-server-gate/tests/integration/main_test.go
+++ b/game/game/pkg/consumer.go
@@ -15,34 +15,38 @@
  * limitations under the License.
  */
 
-package integration
+package pkg
 
 import (
-       "os"
-       "testing"
-)
-
-import (
-       "dubbo.apache.org/dubbo-go/v3/config"
+       "dubbo.apache.org/dubbo-go/v3/client"
        _ "dubbo.apache.org/dubbo-go/v3/imports"
 
-       hessian "github.com/apache/dubbo-go-hessian2"
+       "github.com/dubbogo/gost/log/logger"
 )
 
 import (
-       
"github.com/apache/dubbo-go-samples/compatibility/game/go-server-gate/pkg"
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
+       gateProto "github.com/apache/dubbo-go-samples/game/proto/gate"
 )
 
-var gateProvider = pkg.BasketballService{}
+var GateFootball gateProto.GateService
 
-func TestMain(m *testing.M) {
-       config.SetConsumerService(gateProvider)
+func InitGateClient() (gateProto.GateService, error) {
+       cli, err := client.NewClient(
+               client.WithClientURL("127.0.0.1:20001"),
+       )
+       if err != nil {
+               return nil, err
+       }
 
-       hessian.RegisterPOJO(&pojo.Result{})
-       if err := config.Load(); err != nil {
-               panic(err)
+       svc, err := gateProto.NewGateService(cli)
+       if err != nil {
+               return nil, err
        }
 
-       os.Exit(m.Run())
+       return svc, nil
+}
+
+func SetGateClient(cli gateProto.GateService) {
+       GateFootball = cli
+       logger.Info("gate client initialized")
 }
diff --git a/game/game/pkg/provider.go b/game/game/pkg/provider.go
new file mode 100644
index 00000000..517e026b
--- /dev/null
+++ b/game/game/pkg/provider.go
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pkg
+
+import (
+       "context"
+       "fmt"
+       "strconv"
+)
+
+import (
+       "github.com/dubbogo/gost/log/logger"
+
+       "google.golang.org/protobuf/types/known/structpb"
+)
+
+import (
+       game "github.com/apache/dubbo-go-samples/game/proto/game"
+       gateProto "github.com/apache/dubbo-go-samples/game/proto/gate"
+)
+
+type Info struct {
+       Name  string
+       Score int
+}
+
+type GameServiceHandler struct{}
+
+var userMap = make(map[string]*Info)
+
+func (p *GameServiceHandler) Login(ctx context.Context, req 
*game.LoginRequest) (*game.Result, error) {
+       logger.Infof("message: %#v", req.Uid)
+       var (
+               info *Info
+               ok   bool
+       )
+
+       // call gate service
+       rsp, err := GateFootball.Send(context.TODO(), &gateProto.SendRequest{
+               Uid:  req.Uid,
+               Data: "",
+       })
+       if err != nil {
+               logger.Errorf("send fail: %#s", err.Error())
+               return &game.Result{Code: 1, Msg: err.Error()}, err
+       }
+
+       fmt.Println("receive data from gate:", rsp)
+
+       if info, ok = userMap[req.Uid]; !ok {
+               info = &Info{}
+               info.Name = req.Uid
+               userMap[req.Uid] = info
+       }
+
+       data, _ := structpb.NewStruct(map[string]interface{}{
+               "to":    req.Uid,
+               "score": info.Score,
+       })
+
+       return &game.Result{
+               Code: 0,
+               Msg:  info.Name + ", your score is " + strconv.Itoa(info.Score),
+               Data: data,
+       }, nil
+}
+
+func (p *GameServiceHandler) Score(ctx context.Context, req 
*game.ScoreRequest) (*game.Result, error) {
+       logger.Infof("message: %#v, %#v", req.Uid, req.Score)
+       var (
+               info *Info
+               ok   bool
+       )
+
+       // call gate service
+       rsp, err := GateFootball.Send(context.TODO(), &gateProto.SendRequest{
+               Uid:  req.Uid,
+               Data: req.Score,
+       })
+       if err != nil {
+               logger.Errorf("send fail: %#s", err.Error())
+               return &game.Result{Code: 1, Msg: err.Error()}, err
+       }
+
+       fmt.Println("receive data from gate:", rsp)
+
+       if info, ok = userMap[req.Uid]; !ok {
+               info = &Info{
+                       Name: req.Uid,
+               }
+               userMap[req.Uid] = info
+               logger.Error("user data not found")
+               data, _ := structpb.NewStruct(map[string]interface{}{})
+               return &game.Result{Code: 1, Msg: "user data not found", Data: 
data}, nil
+       }
+       intSource, err := strconv.Atoi(req.Score)
+       if err != nil {
+               logger.Error(err.Error())
+       }
+       info.Score += intSource
+
+       data, _ := structpb.NewStruct(map[string]interface{}{
+               "to":    req.Uid,
+               "score": info.Score,
+       })
+
+       return &game.Result{
+               Code: 0,
+               Msg:  "进球成功",
+               Data: data,
+       }, nil
+}
+
+func (p *GameServiceHandler) Rank(ctx context.Context, req *game.RankRequest) 
(*game.Result, error) {
+       var (
+               rank = 1
+               info *Info
+               ok   bool
+       )
+
+       // call gate service
+       rsp, err := GateFootball.Send(context.TODO(), &gateProto.SendRequest{
+               Uid:  req.Uid,
+               Data: "",
+       })
+       if err != nil {
+               logger.Errorf("send fail: %#s", err.Error())
+               return &game.Result{Code: 1, Msg: err.Error()}, err
+       }
+
+       fmt.Println("receive data from gate:", rsp)
+
+       if info, ok = userMap[req.Uid]; !ok {
+               logger.Error("no user found")
+               data, _ := structpb.NewStruct(map[string]interface{}{
+                       "to":   req.Uid,
+                       "rank": rank,
+               })
+               return &game.Result{Code: 1, Msg: "no user found", Data: data}, 
nil
+       }
+
+       for _, v := range userMap {
+               if v.Score > info.Score {
+                       rank++
+               }
+       }
+
+       data, _ := structpb.NewStruct(map[string]interface{}{
+               "to":   req.Uid,
+               "rank": rank,
+       })
+
+       return &game.Result{
+               Code: 0,
+               Msg:  "success",
+               Data: data,
+       }, nil
+}
diff --git a/compatibility/game/go-server-gate/cmd/server.go 
b/game/gate/go-server/cmd/main.go
similarity index 59%
rename from compatibility/game/go-server-gate/cmd/server.go
rename to game/gate/go-server/cmd/main.go
index a824bc6e..85d08719 100644
--- a/compatibility/game/go-server-gate/cmd/server.go
+++ b/game/gate/go-server/cmd/main.go
@@ -20,46 +20,67 @@ package main
 import (
        "context"
        "encoding/json"
-       "io/ioutil"
+       "io"
        "net/http"
        "os"
        "os/signal"
+       "path/filepath"
        "strconv"
        "syscall"
        "time"
 )
 
 import (
-       "dubbo.apache.org/dubbo-go/v3/config"
        _ "dubbo.apache.org/dubbo-go/v3/imports"
-
-       hessian "github.com/apache/dubbo-go-hessian2"
+       "dubbo.apache.org/dubbo-go/v3/protocol"
+       "dubbo.apache.org/dubbo-go/v3/server"
 
        "github.com/dubbogo/gost/log/logger"
 )
 
 import (
-       
"github.com/apache/dubbo-go-samples/compatibility/game/go-server-gate/pkg"
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
+       "github.com/apache/dubbo-go-samples/game/gate/pkg"
+       gateProto "github.com/apache/dubbo-go-samples/game/proto/gate"
 )
 
-func init() {
-       config.SetProviderService(new(pkg.BasketballService))
-
-       config.SetConsumerService(pkg.GameBasketball)
-
-       hessian.RegisterPOJO(&pojo.Result{})
+type Info struct {
+       Name  string `json:"name"`
+       Score int    `json:"score"`
 }
 
 func main() {
-       err := config.Load()
+       // Initialize game client
+       cli, err := pkg.InitGameClient()
        if err != nil {
-               panic(err)
+               logger.Fatalf("failed to create game client: %v", err)
+       }
+       pkg.SetGameClient(cli)
+
+       // Start RPC server
+       srv, err := server.NewServer(
+               server.WithServerProtocol(
+                       protocol.WithPort(20001),
+                       protocol.WithTriple(),
+               ),
+       )
+       if err != nil {
+               logger.Fatalf("failed to create server: %v", err)
        }
 
+       if err := gateProto.RegisterGateServiceHandler(srv, 
&pkg.GateServiceHandler{}); err != nil {
+               logger.Fatalf("failed to register gate service handler: %v", 
err)
+       }
+
+       // Start HTTP server in goroutine
        go startHttp()
 
-       initSignal()
+       // Start signal handler in goroutine
+       go initSignal()
+
+       // Start RPC server
+       if err := srv.Serve(); err != nil {
+               logger.Fatalf("failed to serve: %v", err)
+       }
 }
 
 func initSignal() {
@@ -86,7 +107,7 @@ func initSignal() {
 }
 
 func startHttp() {
-
+       // Register API endpoints first (before static file server)
        http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
                res, err := pkg.Login(context.TODO(), r.URL.Query().Get("name"))
                if err != nil {
@@ -103,11 +124,11 @@ func startHttp() {
        })
 
        http.HandleFunc("/score", func(w http.ResponseWriter, r *http.Request) {
-               reqBody, err := ioutil.ReadAll(r.Body)
+               reqBody, err := io.ReadAll(r.Body)
                if err != nil {
                        logger.Error(err.Error())
                }
-               var info pojo.Info
+               var info Info
                err = json.Unmarshal(reqBody, &info)
                if err != nil {
                        responseWithOrigin(w, r, 500, []byte(err.Error()))
@@ -141,6 +162,28 @@ func startHttp() {
                responseWithOrigin(w, r, 200, b)
        })
 
+       // Serve static files from website directory (after API endpoints)
+       websiteDir := resolveWebsiteDir()
+       var fs http.Handler
+       if websiteDir != "" {
+               logger.Infof("serving static files from %s", websiteDir)
+               fs = http.FileServer(http.Dir(websiteDir))
+       } else {
+               logger.Warnf("website directory not found, static files 
disabled")
+               fs = http.HandlerFunc(func(w http.ResponseWriter, r 
*http.Request) {
+                       http.NotFound(w, r)
+               })
+       }
+       // Only serve static files for non-API paths
+       http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
+               // Check if it's an API endpoint
+               if r.URL.Path == "/login" || r.URL.Path == "/score" || 
r.URL.Path == "/rank" {
+                       http.NotFound(w, r)
+                       return
+               }
+               fs.ServeHTTP(w, r)
+       })
+
        _ = http.ListenAndServe("127.0.0.1:8089", nil)
 }
 
@@ -158,3 +201,41 @@ func responseWithOrigin(w http.ResponseWriter, r 
*http.Request, code int, json [
                panic(err)
        }
 }
+
+func resolveWebsiteDir() string {
+       if dir := os.Getenv("GAME_WEBSITE_DIR"); dir != "" {
+               return dir
+       }
+
+       var roots []string
+       if wd, err := os.Getwd(); err == nil {
+               roots = append(roots, wd)
+       }
+       if exe, err := os.Executable(); err == nil {
+               roots = append(roots, filepath.Dir(exe))
+       }
+
+       seen := make(map[string]struct{})
+       for _, root := range roots {
+               cur := root
+               for i := 0; i < 6; i++ {
+                       if _, ok := seen[cur]; ok {
+                               break
+                       }
+                       seen[cur] = struct{}{}
+
+                       candidate := filepath.Join(cur, "website")
+                       if info, err := os.Stat(candidate); err == nil && 
info.IsDir() {
+                               return candidate
+                       }
+
+                       parent := filepath.Dir(cur)
+                       if parent == cur {
+                               break
+                       }
+                       cur = parent
+               }
+       }
+
+       return ""
+}
diff --git a/game/gate/pkg/consumer.go b/game/gate/pkg/consumer.go
new file mode 100644
index 00000000..4a3c4a3b
--- /dev/null
+++ b/game/gate/pkg/consumer.go
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package pkg
+
+import (
+       "context"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/client"
+       _ "dubbo.apache.org/dubbo-go/v3/imports"
+
+       "github.com/dubbogo/gost/log/logger"
+)
+
+import (
+       gameProto "github.com/apache/dubbo-go-samples/game/proto/game"
+)
+
+var GameFootball gameProto.GameService
+
+func InitGameClient() (gameProto.GameService, error) {
+       cli, err := client.NewClient(
+               client.WithClientURL("127.0.0.1:20000"),
+       )
+       if err != nil {
+               return nil, err
+       }
+
+       svc, err := gameProto.NewGameService(cli)
+       if err != nil {
+               return nil, err
+       }
+
+       return svc, nil
+}
+
+func SetGameClient(cli gameProto.GameService) {
+       GameFootball = cli
+       logger.Info("game client initialized")
+}
+
+// Helper functions for HTTP handlers
+type Result struct {
+       Code int32                  `json:"code"`
+       Msg  string                 `json:"msg,omitempty"`
+       Data map[string]interface{} `json:"data,omitempty"`
+}
+
+func Login(ctx context.Context, data string) (*Result, error) {
+       resp, err := GameFootball.Login(ctx, &gameProto.LoginRequest{Uid: data})
+       if err != nil {
+               return nil, err
+       }
+       return convertResult(resp), nil
+}
+
+func Score(ctx context.Context, uid, score string) (*Result, error) {
+       resp, err := GameFootball.Score(ctx, &gameProto.ScoreRequest{Uid: uid, 
Score: score})
+       if err != nil {
+               return nil, err
+       }
+       return convertResult(resp), nil
+}
+
+func Rank(ctx context.Context, uid string) (*Result, error) {
+       resp, err := GameFootball.Rank(ctx, &gameProto.RankRequest{Uid: uid})
+       if err != nil {
+               return nil, err
+       }
+       return convertResult(resp), nil
+}
+
+func convertResult(resp *gameProto.Result) *Result {
+       result := &Result{
+               Code: resp.Code,
+               Msg:  resp.Msg,
+       }
+       if resp.Data != nil {
+               result.Data = resp.Data.AsMap()
+       }
+       return result
+}
diff --git a/compatibility/game/go-server-gate/pkg/provider.go 
b/game/gate/pkg/provider.go
similarity index 65%
rename from compatibility/game/go-server-gate/pkg/provider.go
rename to game/gate/pkg/provider.go
index 60857bf6..19612bb7 100644
--- a/compatibility/game/go-server-gate/pkg/provider.go
+++ b/game/gate/pkg/provider.go
@@ -23,19 +23,21 @@ import (
 
 import (
        "github.com/dubbogo/gost/log/logger"
+
+       "google.golang.org/protobuf/types/known/structpb"
 )
 
 import (
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
+       gateProto "github.com/apache/dubbo-go-samples/game/proto/gate"
 )
 
-type BasketballService struct{}
-
-func (p *BasketballService) Send(ctx context.Context, uid, data string) 
(*pojo.Result, error) {
-       logger.Infof("basketball: to=%s, message=%s", uid, data)
-       return &pojo.Result{Code: 0, Data: map[string]interface{}{"to": uid, 
"message": data}}, nil
-}
+type GateServiceHandler struct{}
 
-func (p *BasketballService) Reference() string {
-       return "gateProvider.basketballService"
+func (p *GateServiceHandler) Send(ctx context.Context, req 
*gateProto.SendRequest) (*gateProto.Result, error) {
+       logger.Infof("football: to=%s, message=%s", req.Uid, req.Data)
+       data, _ := structpb.NewStruct(map[string]interface{}{
+               "to":      req.Uid,
+               "message": req.Data,
+       })
+       return &gateProto.Result{Code: 0, Data: data}, nil
 }
diff --git a/game/proto/game/game.pb.go b/game/proto/game/game.pb.go
new file mode 100644
index 00000000..0b35f088
--- /dev/null
+++ b/game/proto/game/game.pb.go
@@ -0,0 +1,395 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+//     protoc-gen-go v1.31.0
+//     protoc        v3.21.12
+// source: game.proto
+
+package game
+
+import (
+       protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+       protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+       structpb "google.golang.org/protobuf/types/known/structpb"
+       reflect "reflect"
+       sync "sync"
+)
+
+const (
+       // Verify that this generated code is sufficiently up-to-date.
+       _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+       // Verify that runtime/protoimpl is sufficiently up-to-date.
+       _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Result struct {
+       state         protoimpl.MessageState
+       sizeCache     protoimpl.SizeCache
+       unknownFields protoimpl.UnknownFields
+
+       Code int32            `protobuf:"varint,1,opt,name=code,proto3" 
json:"code,omitempty"` // 0: success, [1, *): error
+       Msg  string           `protobuf:"bytes,2,opt,name=msg,proto3" 
json:"msg,omitempty"`
+       Data *structpb.Struct `protobuf:"bytes,3,opt,name=data,proto3" 
json:"data,omitempty"`
+}
+
+func (x *Result) Reset() {
+       *x = Result{}
+       if protoimpl.UnsafeEnabled {
+               mi := &file_game_proto_msgTypes[0]
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               ms.StoreMessageInfo(mi)
+       }
+}
+
+func (x *Result) String() string {
+       return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Result) ProtoMessage() {}
+
+func (x *Result) ProtoReflect() protoreflect.Message {
+       mi := &file_game_proto_msgTypes[0]
+       if protoimpl.UnsafeEnabled && x != nil {
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               if ms.LoadMessageInfo() == nil {
+                       ms.StoreMessageInfo(mi)
+               }
+               return ms
+       }
+       return mi.MessageOf(x)
+}
+
+// Deprecated: Use Result.ProtoReflect.Descriptor instead.
+func (*Result) Descriptor() ([]byte, []int) {
+       return file_game_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Result) GetCode() int32 {
+       if x != nil {
+               return x.Code
+       }
+       return 0
+}
+
+func (x *Result) GetMsg() string {
+       if x != nil {
+               return x.Msg
+       }
+       return ""
+}
+
+func (x *Result) GetData() *structpb.Struct {
+       if x != nil {
+               return x.Data
+       }
+       return nil
+}
+
+type LoginRequest struct {
+       state         protoimpl.MessageState
+       sizeCache     protoimpl.SizeCache
+       unknownFields protoimpl.UnknownFields
+
+       Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"`
+}
+
+func (x *LoginRequest) Reset() {
+       *x = LoginRequest{}
+       if protoimpl.UnsafeEnabled {
+               mi := &file_game_proto_msgTypes[1]
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               ms.StoreMessageInfo(mi)
+       }
+}
+
+func (x *LoginRequest) String() string {
+       return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LoginRequest) ProtoMessage() {}
+
+func (x *LoginRequest) ProtoReflect() protoreflect.Message {
+       mi := &file_game_proto_msgTypes[1]
+       if protoimpl.UnsafeEnabled && x != nil {
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               if ms.LoadMessageInfo() == nil {
+                       ms.StoreMessageInfo(mi)
+               }
+               return ms
+       }
+       return mi.MessageOf(x)
+}
+
+// Deprecated: Use LoginRequest.ProtoReflect.Descriptor instead.
+func (*LoginRequest) Descriptor() ([]byte, []int) {
+       return file_game_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *LoginRequest) GetUid() string {
+       if x != nil {
+               return x.Uid
+       }
+       return ""
+}
+
+type ScoreRequest struct {
+       state         protoimpl.MessageState
+       sizeCache     protoimpl.SizeCache
+       unknownFields protoimpl.UnknownFields
+
+       Uid   string `protobuf:"bytes,1,opt,name=uid,proto3" 
json:"uid,omitempty"`
+       Score string `protobuf:"bytes,2,opt,name=score,proto3" 
json:"score,omitempty"`
+}
+
+func (x *ScoreRequest) Reset() {
+       *x = ScoreRequest{}
+       if protoimpl.UnsafeEnabled {
+               mi := &file_game_proto_msgTypes[2]
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               ms.StoreMessageInfo(mi)
+       }
+}
+
+func (x *ScoreRequest) String() string {
+       return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ScoreRequest) ProtoMessage() {}
+
+func (x *ScoreRequest) ProtoReflect() protoreflect.Message {
+       mi := &file_game_proto_msgTypes[2]
+       if protoimpl.UnsafeEnabled && x != nil {
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               if ms.LoadMessageInfo() == nil {
+                       ms.StoreMessageInfo(mi)
+               }
+               return ms
+       }
+       return mi.MessageOf(x)
+}
+
+// Deprecated: Use ScoreRequest.ProtoReflect.Descriptor instead.
+func (*ScoreRequest) Descriptor() ([]byte, []int) {
+       return file_game_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *ScoreRequest) GetUid() string {
+       if x != nil {
+               return x.Uid
+       }
+       return ""
+}
+
+func (x *ScoreRequest) GetScore() string {
+       if x != nil {
+               return x.Score
+       }
+       return ""
+}
+
+type RankRequest struct {
+       state         protoimpl.MessageState
+       sizeCache     protoimpl.SizeCache
+       unknownFields protoimpl.UnknownFields
+
+       Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"`
+}
+
+func (x *RankRequest) Reset() {
+       *x = RankRequest{}
+       if protoimpl.UnsafeEnabled {
+               mi := &file_game_proto_msgTypes[3]
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               ms.StoreMessageInfo(mi)
+       }
+}
+
+func (x *RankRequest) String() string {
+       return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RankRequest) ProtoMessage() {}
+
+func (x *RankRequest) ProtoReflect() protoreflect.Message {
+       mi := &file_game_proto_msgTypes[3]
+       if protoimpl.UnsafeEnabled && x != nil {
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               if ms.LoadMessageInfo() == nil {
+                       ms.StoreMessageInfo(mi)
+               }
+               return ms
+       }
+       return mi.MessageOf(x)
+}
+
+// Deprecated: Use RankRequest.ProtoReflect.Descriptor instead.
+func (*RankRequest) Descriptor() ([]byte, []int) {
+       return file_game_proto_rawDescGZIP(), []int{3}
+}
+
+func (x *RankRequest) GetUid() string {
+       if x != nil {
+               return x.Uid
+       }
+       return ""
+}
+
+var File_game_proto protoreflect.FileDescriptor
+
+var file_game_proto_rawDesc = []byte{
+       0x0a, 0x0a, 0x67, 0x61, 0x6d, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 
0x12, 0x04, 0x67, 0x61,
+       0x6d, 0x65, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 
0x72, 0x6f, 0x74, 0x6f,
+       0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 
0x72, 0x6f, 0x74, 0x6f,
+       0x22, 0x5b, 0x0a, 0x06, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x12, 0x12, 
0x0a, 0x04, 0x63, 0x6f,
+       0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x63, 0x6f, 
0x64, 0x65, 0x12, 0x10,
+       0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 
0x03, 0x6d, 0x73, 0x67,
+       0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 
0x28, 0x0b, 0x32, 0x17,
+       0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 
0x6f, 0x62, 0x75, 0x66,
+       0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x04, 0x64, 0x61, 0x74, 
0x61, 0x22, 0x20, 0x0a,
+       0x0c, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 
0x74, 0x12, 0x10, 0x0a,
+       0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 
0x75, 0x69, 0x64, 0x22,
+       0x36, 0x0a, 0x0c, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x52, 0x65, 0x71, 0x75, 
0x65, 0x73, 0x74, 0x12,
+       0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 
0x52, 0x03, 0x75, 0x69,
+       0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x18, 0x02, 
0x20, 0x01, 0x28, 0x09,
+       0x52, 0x05, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x22, 0x1f, 0x0a, 0x0b, 0x52, 
0x61, 0x6e, 0x6b, 0x52,
+       0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 
0x64, 0x18, 0x01, 0x20,
+       0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x32, 0x92, 0x01, 0x0a, 
0x0b, 0x47, 0x61, 0x6d,
+       0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x2b, 0x0a, 0x05, 
0x4c, 0x6f, 0x67, 0x69,
+       0x6e, 0x12, 0x12, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 
0x69, 0x6e, 0x52, 0x65,
+       0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x67, 0x61, 0x6d, 0x65, 
0x2e, 0x52, 0x65, 0x73,
+       0x75, 0x6c, 0x74, 0x22, 0x00, 0x12, 0x2b, 0x0a, 0x05, 0x53, 0x63, 0x6f, 
0x72, 0x65, 0x12, 0x12,
+       0x2e, 0x67, 0x61, 0x6d, 0x65, 0x2e, 0x53, 0x63, 0x6f, 0x72, 0x65, 0x52, 
0x65, 0x71, 0x75, 0x65,
+       0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x67, 0x61, 0x6d, 0x65, 0x2e, 0x52, 0x65, 
0x73, 0x75, 0x6c, 0x74,
+       0x22, 0x00, 0x12, 0x29, 0x0a, 0x04, 0x52, 0x61, 0x6e, 0x6b, 0x12, 0x11, 
0x2e, 0x67, 0x61, 0x6d,
+       0x65, 0x2e, 0x52, 0x61, 0x6e, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 
0x74, 0x1a, 0x0c, 0x2e,
+       0x67, 0x61, 0x6d, 0x65, 0x2e, 0x52, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x22, 
0x00, 0x42, 0x39, 0x5a,
+       0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 
0x61, 0x70, 0x61, 0x63,
+       0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 0x2d, 0x67, 0x6f, 0x2d, 
0x73, 0x61, 0x6d, 0x70,
+       0x6c, 0x65, 0x73, 0x2f, 0x67, 0x61, 0x6d, 0x65, 0x2f, 0x70, 0x72, 0x6f, 
0x74, 0x6f, 0x2f, 0x67,
+       0x61, 0x6d, 0x65, 0x3b, 0x67, 0x61, 0x6d, 0x65, 0x62, 0x06, 0x70, 0x72, 
0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+       file_game_proto_rawDescOnce sync.Once
+       file_game_proto_rawDescData = file_game_proto_rawDesc
+)
+
+func file_game_proto_rawDescGZIP() []byte {
+       file_game_proto_rawDescOnce.Do(func() {
+               file_game_proto_rawDescData = 
protoimpl.X.CompressGZIP(file_game_proto_rawDescData)
+       })
+       return file_game_proto_rawDescData
+}
+
+var file_game_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
+var file_game_proto_goTypes = []interface{}{
+       (*Result)(nil),          // 0: game.Result
+       (*LoginRequest)(nil),    // 1: game.LoginRequest
+       (*ScoreRequest)(nil),    // 2: game.ScoreRequest
+       (*RankRequest)(nil),     // 3: game.RankRequest
+       (*structpb.Struct)(nil), // 4: google.protobuf.Struct
+}
+var file_game_proto_depIdxs = []int32{
+       4, // 0: game.Result.data:type_name -> google.protobuf.Struct
+       1, // 1: game.GameService.Login:input_type -> game.LoginRequest
+       2, // 2: game.GameService.Score:input_type -> game.ScoreRequest
+       3, // 3: game.GameService.Rank:input_type -> game.RankRequest
+       0, // 4: game.GameService.Login:output_type -> game.Result
+       0, // 5: game.GameService.Score:output_type -> game.Result
+       0, // 6: game.GameService.Rank:output_type -> game.Result
+       4, // [4:7] is the sub-list for method output_type
+       1, // [1:4] is the sub-list for method input_type
+       1, // [1:1] is the sub-list for extension type_name
+       1, // [1:1] is the sub-list for extension extendee
+       0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_game_proto_init() }
+func file_game_proto_init() {
+       if File_game_proto != nil {
+               return
+       }
+       if !protoimpl.UnsafeEnabled {
+               file_game_proto_msgTypes[0].Exporter = func(v interface{}, i 
int) interface{} {
+                       switch v := v.(*Result); i {
+                       case 0:
+                               return &v.state
+                       case 1:
+                               return &v.sizeCache
+                       case 2:
+                               return &v.unknownFields
+                       default:
+                               return nil
+                       }
+               }
+               file_game_proto_msgTypes[1].Exporter = func(v interface{}, i 
int) interface{} {
+                       switch v := v.(*LoginRequest); i {
+                       case 0:
+                               return &v.state
+                       case 1:
+                               return &v.sizeCache
+                       case 2:
+                               return &v.unknownFields
+                       default:
+                               return nil
+                       }
+               }
+               file_game_proto_msgTypes[2].Exporter = func(v interface{}, i 
int) interface{} {
+                       switch v := v.(*ScoreRequest); i {
+                       case 0:
+                               return &v.state
+                       case 1:
+                               return &v.sizeCache
+                       case 2:
+                               return &v.unknownFields
+                       default:
+                               return nil
+                       }
+               }
+               file_game_proto_msgTypes[3].Exporter = func(v interface{}, i 
int) interface{} {
+                       switch v := v.(*RankRequest); i {
+                       case 0:
+                               return &v.state
+                       case 1:
+                               return &v.sizeCache
+                       case 2:
+                               return &v.unknownFields
+                       default:
+                               return nil
+                       }
+               }
+       }
+       type x struct{}
+       out := protoimpl.TypeBuilder{
+               File: protoimpl.DescBuilder{
+                       GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+                       RawDescriptor: file_game_proto_rawDesc,
+                       NumEnums:      0,
+                       NumMessages:   4,
+                       NumExtensions: 0,
+                       NumServices:   1,
+               },
+               GoTypes:           file_game_proto_goTypes,
+               DependencyIndexes: file_game_proto_depIdxs,
+               MessageInfos:      file_game_proto_msgTypes,
+       }.Build()
+       File_game_proto = out.File
+       file_game_proto_rawDesc = nil
+       file_game_proto_goTypes = nil
+       file_game_proto_depIdxs = nil
+}
diff --git 
a/integrate_test/compatibility/game/go-server-game/tests/integration/main_test.go
 b/game/proto/game/game.proto
similarity index 58%
copy from 
integrate_test/compatibility/game/go-server-game/tests/integration/main_test.go
copy to game/proto/game/game.proto
index 14c98300..e3ad76fc 100644
--- 
a/integrate_test/compatibility/game/go-server-game/tests/integration/main_test.go
+++ b/game/proto/game/game.proto
@@ -15,35 +15,36 @@
  * limitations under the License.
  */
 
-package integration
+syntax = "proto3";
 
-import (
-       "os"
-       "testing"
-)
+package game;
 
-import (
-       "dubbo.apache.org/dubbo-go/v3/config"
-       _ "dubbo.apache.org/dubbo-go/v3/imports"
+option go_package = "github.com/apache/dubbo-go-samples/game/proto/game;game";
 
-       hessian "github.com/apache/dubbo-go-hessian2"
-)
+import "google/protobuf/struct.proto";
 
-import (
-       
"github.com/apache/dubbo-go-samples/compatibility/game/go-server-game/pkg"
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
-)
+message Result {
+  int32 code = 1; // 0: success, [1, *): error
+  string msg = 2;
+  google.protobuf.Struct data = 3;
+}
 
-var gameProvider = pkg.BasketballService{}
+message LoginRequest {
+  string uid = 1;
+}
 
-func TestMain(m *testing.M) {
-       config.SetConsumerService(gameProvider)
+message ScoreRequest {
+  string uid = 1;
+  string score = 2;
+}
 
-       hessian.RegisterPOJO(&pojo.Result{})
-       err := config.Load()
-       if err != nil {
-               panic(err)
-       }
+message RankRequest {
+  string uid = 1;
+}
 
-       os.Exit(m.Run())
+service GameService {
+  rpc Login(LoginRequest) returns (Result) {}
+  rpc Score(ScoreRequest) returns (Result) {}
+  rpc Rank(RankRequest) returns (Result) {}
 }
+
diff --git a/game/proto/game/game.triple.go b/game/proto/game/game.triple.go
new file mode 100644
index 00000000..2bdebe9d
--- /dev/null
+++ b/game/proto/game/game.triple.go
@@ -0,0 +1,176 @@
+// Code generated by protoc-gen-triple. DO NOT EDIT.
+//
+// Source: game.proto
+package game
+
+import (
+       "context"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3"
+       "dubbo.apache.org/dubbo-go/v3/client"
+       "dubbo.apache.org/dubbo-go/v3/common"
+       "dubbo.apache.org/dubbo-go/v3/common/constant"
+       "dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol"
+       "dubbo.apache.org/dubbo-go/v3/server"
+)
+
+// This is a compile-time assertion to ensure that this generated file and the 
Triple package
+// are compatible. If you get a compiler error that this constant is not 
defined, this code was
+// generated with a version of Triple newer than the one compiled into your 
binary. You can fix the
+// problem by either regenerating this code with an older version of Triple or 
updating the Triple
+// version compiled into your binary.
+const _ = triple_protocol.IsAtLeastVersion0_1_0
+
+const (
+       // GameServiceName is the fully-qualified name of the GameService 
service.
+       GameServiceName = "game.GameService"
+)
+
+// These constants are the fully-qualified names of the RPCs defined in this 
package. They're
+// exposed at runtime as procedure and as the final two segments of the HTTP 
route.
+//
+// Note that these are different from the fully-qualified method names used by
+// google.golang.org/protobuf/reflect/protoreflect. To convert from these 
constants to
+// reflection-formatted method names, remove the leading slash and convert the 
remaining slash to a
+// period.
+const (
+       // GameServiceLoginProcedure is the fully-qualified name of the 
GameService's Login RPC.
+       GameServiceLoginProcedure = "/game.GameService/Login"
+       // GameServiceScoreProcedure is the fully-qualified name of the 
GameService's Score RPC.
+       GameServiceScoreProcedure = "/game.GameService/Score"
+       // GameServiceRankProcedure is the fully-qualified name of the 
GameService's Rank RPC.
+       GameServiceRankProcedure = "/game.GameService/Rank"
+)
+
+var (
+       _ GameService = (*GameServiceImpl)(nil)
+)
+
+// GameService is a client for the game.GameService service.
+type GameService interface {
+       Login(ctx context.Context, req *LoginRequest, opts 
...client.CallOption) (*Result, error)
+       Score(ctx context.Context, req *ScoreRequest, opts 
...client.CallOption) (*Result, error)
+       Rank(ctx context.Context, req *RankRequest, opts ...client.CallOption) 
(*Result, error)
+}
+
+// NewGameService constructs a client for the game.GameService service.
+func NewGameService(cli *client.Client, opts ...client.ReferenceOption) 
(GameService, error) {
+       conn, err := cli.DialWithInfo("game.GameService", 
&GameService_ClientInfo, opts...)
+       if err != nil {
+               return nil, err
+       }
+       return &GameServiceImpl{
+               conn: conn,
+       }, nil
+}
+
+func SetConsumerGameService(srv common.RPCService) {
+       dubbo.SetConsumerServiceWithInfo(srv, &GameService_ClientInfo)
+}
+
+// GameServiceImpl implements GameService.
+type GameServiceImpl struct {
+       conn *client.Connection
+}
+
+func (c *GameServiceImpl) Login(ctx context.Context, req *LoginRequest, opts 
...client.CallOption) (*Result, error) {
+       resp := new(Result)
+       if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "Login", 
opts...); err != nil {
+               return nil, err
+       }
+       return resp, nil
+}
+
+func (c *GameServiceImpl) Score(ctx context.Context, req *ScoreRequest, opts 
...client.CallOption) (*Result, error) {
+       resp := new(Result)
+       if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "Score", 
opts...); err != nil {
+               return nil, err
+       }
+       return resp, nil
+}
+
+func (c *GameServiceImpl) Rank(ctx context.Context, req *RankRequest, opts 
...client.CallOption) (*Result, error) {
+       resp := new(Result)
+       if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "Rank", 
opts...); err != nil {
+               return nil, err
+       }
+       return resp, nil
+}
+
+var GameService_ClientInfo = client.ClientInfo{
+       InterfaceName: "game.GameService",
+       MethodNames:   []string{"Login", "Score", "Rank"},
+       ConnectionInjectFunc: func(dubboCliRaw interface{}, conn 
*client.Connection) {
+               dubboCli := dubboCliRaw.(*GameServiceImpl)
+               dubboCli.conn = conn
+       },
+}
+
+// GameServiceHandler is an implementation of the game.GameService service.
+type GameServiceHandler interface {
+       Login(context.Context, *LoginRequest) (*Result, error)
+       Score(context.Context, *ScoreRequest) (*Result, error)
+       Rank(context.Context, *RankRequest) (*Result, error)
+}
+
+func RegisterGameServiceHandler(srv *server.Server, hdlr GameServiceHandler, 
opts ...server.ServiceOption) error {
+       return srv.Register(hdlr, &GameService_ServiceInfo, opts...)
+}
+
+func SetProviderGameService(srv common.RPCService) {
+       dubbo.SetProviderServiceWithInfo(srv, &GameService_ServiceInfo)
+}
+
+var GameService_ServiceInfo = server.ServiceInfo{
+       InterfaceName: "game.GameService",
+       ServiceType:   (*GameServiceHandler)(nil),
+       Methods: []server.MethodInfo{
+               {
+                       Name: "Login",
+                       Type: constant.CallUnary,
+                       ReqInitFunc: func() interface{} {
+                               return new(LoginRequest)
+                       },
+                       MethodFunc: func(ctx context.Context, args 
[]interface{}, handler interface{}) (interface{}, error) {
+                               req := args[0].(*LoginRequest)
+                               res, err := 
handler.(GameServiceHandler).Login(ctx, req)
+                               if err != nil {
+                                       return nil, err
+                               }
+                               return triple_protocol.NewResponse(res), nil
+                       },
+               },
+               {
+                       Name: "Score",
+                       Type: constant.CallUnary,
+                       ReqInitFunc: func() interface{} {
+                               return new(ScoreRequest)
+                       },
+                       MethodFunc: func(ctx context.Context, args 
[]interface{}, handler interface{}) (interface{}, error) {
+                               req := args[0].(*ScoreRequest)
+                               res, err := 
handler.(GameServiceHandler).Score(ctx, req)
+                               if err != nil {
+                                       return nil, err
+                               }
+                               return triple_protocol.NewResponse(res), nil
+                       },
+               },
+               {
+                       Name: "Rank",
+                       Type: constant.CallUnary,
+                       ReqInitFunc: func() interface{} {
+                               return new(RankRequest)
+                       },
+                       MethodFunc: func(ctx context.Context, args 
[]interface{}, handler interface{}) (interface{}, error) {
+                               req := args[0].(*RankRequest)
+                               res, err := 
handler.(GameServiceHandler).Rank(ctx, req)
+                               if err != nil {
+                                       return nil, err
+                               }
+                               return triple_protocol.NewResponse(res), nil
+                       },
+               },
+       },
+}
diff --git a/game/proto/gate/gate.pb.go b/game/proto/gate/gate.pb.go
new file mode 100644
index 00000000..980ff10a
--- /dev/null
+++ b/game/proto/gate/gate.pb.go
@@ -0,0 +1,262 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one or more
+// contributor license agreements.  See the NOTICE file distributed with
+// this work for additional information regarding copyright ownership.
+// The ASF licenses this file to You under the Apache License, Version 2.0
+// (the "License"); you may not use this file except in compliance with
+// the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+//     protoc-gen-go v1.31.0
+//     protoc        v3.21.12
+// source: gate/gate.proto
+
+package gate
+
+import (
+       protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+       protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+       structpb "google.golang.org/protobuf/types/known/structpb"
+       reflect "reflect"
+       sync "sync"
+)
+
+const (
+       // Verify that this generated code is sufficiently up-to-date.
+       _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+       // Verify that runtime/protoimpl is sufficiently up-to-date.
+       _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+type Result struct {
+       state         protoimpl.MessageState
+       sizeCache     protoimpl.SizeCache
+       unknownFields protoimpl.UnknownFields
+
+       Code int32            `protobuf:"varint,1,opt,name=code,proto3" 
json:"code,omitempty"` // 0: success, [1, *): error
+       Msg  string           `protobuf:"bytes,2,opt,name=msg,proto3" 
json:"msg,omitempty"`
+       Data *structpb.Struct `protobuf:"bytes,3,opt,name=data,proto3" 
json:"data,omitempty"`
+}
+
+func (x *Result) Reset() {
+       *x = Result{}
+       if protoimpl.UnsafeEnabled {
+               mi := &file_gate_gate_proto_msgTypes[0]
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               ms.StoreMessageInfo(mi)
+       }
+}
+
+func (x *Result) String() string {
+       return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Result) ProtoMessage() {}
+
+func (x *Result) ProtoReflect() protoreflect.Message {
+       mi := &file_gate_gate_proto_msgTypes[0]
+       if protoimpl.UnsafeEnabled && x != nil {
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               if ms.LoadMessageInfo() == nil {
+                       ms.StoreMessageInfo(mi)
+               }
+               return ms
+       }
+       return mi.MessageOf(x)
+}
+
+// Deprecated: Use Result.ProtoReflect.Descriptor instead.
+func (*Result) Descriptor() ([]byte, []int) {
+       return file_gate_gate_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Result) GetCode() int32 {
+       if x != nil {
+               return x.Code
+       }
+       return 0
+}
+
+func (x *Result) GetMsg() string {
+       if x != nil {
+               return x.Msg
+       }
+       return ""
+}
+
+func (x *Result) GetData() *structpb.Struct {
+       if x != nil {
+               return x.Data
+       }
+       return nil
+}
+
+type SendRequest struct {
+       state         protoimpl.MessageState
+       sizeCache     protoimpl.SizeCache
+       unknownFields protoimpl.UnknownFields
+
+       Uid  string `protobuf:"bytes,1,opt,name=uid,proto3" 
json:"uid,omitempty"`
+       Data string `protobuf:"bytes,2,opt,name=data,proto3" 
json:"data,omitempty"`
+}
+
+func (x *SendRequest) Reset() {
+       *x = SendRequest{}
+       if protoimpl.UnsafeEnabled {
+               mi := &file_gate_gate_proto_msgTypes[1]
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               ms.StoreMessageInfo(mi)
+       }
+}
+
+func (x *SendRequest) String() string {
+       return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SendRequest) ProtoMessage() {}
+
+func (x *SendRequest) ProtoReflect() protoreflect.Message {
+       mi := &file_gate_gate_proto_msgTypes[1]
+       if protoimpl.UnsafeEnabled && x != nil {
+               ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+               if ms.LoadMessageInfo() == nil {
+                       ms.StoreMessageInfo(mi)
+               }
+               return ms
+       }
+       return mi.MessageOf(x)
+}
+
+// Deprecated: Use SendRequest.ProtoReflect.Descriptor instead.
+func (*SendRequest) Descriptor() ([]byte, []int) {
+       return file_gate_gate_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *SendRequest) GetUid() string {
+       if x != nil {
+               return x.Uid
+       }
+       return ""
+}
+
+func (x *SendRequest) GetData() string {
+       if x != nil {
+               return x.Data
+       }
+       return ""
+}
+
+var File_gate_gate_proto protoreflect.FileDescriptor
+
+var file_gate_gate_proto_rawDesc = []byte{
+       0x0a, 0x0f, 0x67, 0x61, 0x74, 0x65, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x2e, 
0x70, 0x72, 0x6f, 0x74,
+       0x6f, 0x12, 0x04, 0x67, 0x61, 0x74, 0x65, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 
0x67, 0x6c, 0x65, 0x2f,
+       0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 
0x75, 0x63, 0x74, 0x2e,
+       0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x5b, 0x0a, 0x06, 0x52, 0x65, 0x73, 
0x75, 0x6c, 0x74, 0x12,
+       0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 
0x05, 0x52, 0x04, 0x63,
+       0x6f, 0x64, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x73, 0x67, 0x18, 0x02, 
0x20, 0x01, 0x28, 0x09,
+       0x52, 0x03, 0x6d, 0x73, 0x67, 0x12, 0x2b, 0x0a, 0x04, 0x64, 0x61, 0x74, 
0x61, 0x18, 0x03, 0x20,
+       0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 
0x2e, 0x70, 0x72, 0x6f,
+       0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 
0x52, 0x04, 0x64, 0x61,
+       0x74, 0x61, 0x22, 0x33, 0x0a, 0x0b, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 
0x71, 0x75, 0x65, 0x73,
+       0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 
0x28, 0x09, 0x52, 0x03,
+       0x75, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x61, 0x18, 
0x02, 0x20, 0x01, 0x28,
+       0x09, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x32, 0x38, 0x0a, 0x0b, 0x47, 
0x61, 0x74, 0x65, 0x53,
+       0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x29, 0x0a, 0x04, 0x53, 0x65, 
0x6e, 0x64, 0x12, 0x11,
+       0x2e, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 
0x71, 0x75, 0x65, 0x73,
+       0x74, 0x1a, 0x0c, 0x2e, 0x67, 0x61, 0x74, 0x65, 0x2e, 0x52, 0x65, 0x73, 
0x75, 0x6c, 0x74, 0x22,
+       0x00, 0x42, 0x39, 0x5a, 0x37, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 
0x63, 0x6f, 0x6d, 0x2f,
+       0x61, 0x70, 0x61, 0x63, 0x68, 0x65, 0x2f, 0x64, 0x75, 0x62, 0x62, 0x6f, 
0x2d, 0x67, 0x6f, 0x2d,
+       0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x67, 0x61, 0x6d, 0x65, 
0x2f, 0x70, 0x72, 0x6f,
+       0x74, 0x6f, 0x2f, 0x67, 0x61, 0x74, 0x65, 0x3b, 0x67, 0x61, 0x74, 0x65, 
0x62, 0x06, 0x70, 0x72,
+       0x6f, 0x74, 0x6f, 0x33,
+}
+
+var (
+       file_gate_gate_proto_rawDescOnce sync.Once
+       file_gate_gate_proto_rawDescData = file_gate_gate_proto_rawDesc
+)
+
+func file_gate_gate_proto_rawDescGZIP() []byte {
+       file_gate_gate_proto_rawDescOnce.Do(func() {
+               file_gate_gate_proto_rawDescData = 
protoimpl.X.CompressGZIP(file_gate_gate_proto_rawDescData)
+       })
+       return file_gate_gate_proto_rawDescData
+}
+
+var file_gate_gate_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_gate_gate_proto_goTypes = []interface{}{
+       (*Result)(nil),          // 0: gate.Result
+       (*SendRequest)(nil),     // 1: gate.SendRequest
+       (*structpb.Struct)(nil), // 2: google.protobuf.Struct
+}
+var file_gate_gate_proto_depIdxs = []int32{
+       2, // 0: gate.Result.data:type_name -> google.protobuf.Struct
+       1, // 1: gate.GateService.Send:input_type -> gate.SendRequest
+       0, // 2: gate.GateService.Send:output_type -> gate.Result
+       2, // [2:3] is the sub-list for method output_type
+       1, // [1:2] is the sub-list for method input_type
+       1, // [1:1] is the sub-list for extension type_name
+       1, // [1:1] is the sub-list for extension extendee
+       0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_gate_gate_proto_init() }
+func file_gate_gate_proto_init() {
+       if File_gate_gate_proto != nil {
+               return
+       }
+       if !protoimpl.UnsafeEnabled {
+               file_gate_gate_proto_msgTypes[0].Exporter = func(v interface{}, 
i int) interface{} {
+                       switch v := v.(*Result); i {
+                       case 0:
+                               return &v.state
+                       case 1:
+                               return &v.sizeCache
+                       case 2:
+                               return &v.unknownFields
+                       default:
+                               return nil
+                       }
+               }
+               file_gate_gate_proto_msgTypes[1].Exporter = func(v interface{}, 
i int) interface{} {
+                       switch v := v.(*SendRequest); i {
+                       case 0:
+                               return &v.state
+                       case 1:
+                               return &v.sizeCache
+                       case 2:
+                               return &v.unknownFields
+                       default:
+                               return nil
+                       }
+               }
+       }
+       type x struct{}
+       out := protoimpl.TypeBuilder{
+               File: protoimpl.DescBuilder{
+                       GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+                       RawDescriptor: file_gate_gate_proto_rawDesc,
+                       NumEnums:      0,
+                       NumMessages:   2,
+                       NumExtensions: 0,
+                       NumServices:   1,
+               },
+               GoTypes:           file_gate_gate_proto_goTypes,
+               DependencyIndexes: file_gate_gate_proto_depIdxs,
+               MessageInfos:      file_gate_gate_proto_msgTypes,
+       }.Build()
+       File_gate_gate_proto = out.File
+       file_gate_gate_proto_rawDesc = nil
+       file_gate_gate_proto_goTypes = nil
+       file_gate_gate_proto_depIdxs = nil
+}
diff --git a/compatibility/game/pkg/pojo/result.go b/game/proto/gate/gate.proto
similarity index 67%
rename from compatibility/game/pkg/pojo/result.go
rename to game/proto/gate/gate.proto
index 7b4e2b4b..9b93297f 100644
--- a/compatibility/game/pkg/pojo/result.go
+++ b/game/proto/gate/gate.proto
@@ -15,18 +15,26 @@
  * limitations under the License.
  */
 
-package pojo
+syntax = "proto3";
 
-type Result struct {
-       Code int32                  `json:"code"` // 0: success, [1, *): error
-       Msg  string                 `json:"msg,omitempty"`
-       Data map[string]interface{} `json:"data,omitempty"`
+package gate;
+
+option go_package = "github.com/apache/dubbo-go-samples/game/proto/gate;gate";
+
+import "google/protobuf/struct.proto";
+
+message Result {
+  int32 code = 1; // 0: success, [1, *): error
+  string msg = 2;
+  google.protobuf.Struct data = 3;
 }
 
-func (m *Result) JavaClassName() string {
-       return "org.apache.dubbo.pojo.Result"
+message SendRequest {
+  string uid = 1;
+  string data = 2;
 }
 
-func (m *Result) Success() bool {
-       return m.Code == 0
+service GateService {
+  rpc Send(SendRequest) returns (Result) {}
 }
+
diff --git a/game/proto/gate/gate.triple.go b/game/proto/gate/gate.triple.go
new file mode 100644
index 00000000..78309a15
--- /dev/null
+++ b/game/proto/gate/gate.triple.go
@@ -0,0 +1,122 @@
+// Code generated by protoc-gen-triple. DO NOT EDIT.
+//
+// Source: gate/gate.proto
+package gate
+
+import (
+       "context"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3"
+       "dubbo.apache.org/dubbo-go/v3/client"
+       "dubbo.apache.org/dubbo-go/v3/common"
+       "dubbo.apache.org/dubbo-go/v3/common/constant"
+       "dubbo.apache.org/dubbo-go/v3/protocol/triple/triple_protocol"
+       "dubbo.apache.org/dubbo-go/v3/server"
+)
+
+// This is a compile-time assertion to ensure that this generated file and the 
Triple package
+// are compatible. If you get a compiler error that this constant is not 
defined, this code was
+// generated with a version of Triple newer than the one compiled into your 
binary. You can fix the
+// problem by either regenerating this code with an older version of Triple or 
updating the Triple
+// version compiled into your binary.
+const _ = triple_protocol.IsAtLeastVersion0_1_0
+
+const (
+       // GateServiceName is the fully-qualified name of the GateService 
service.
+       GateServiceName = "gate.GateService"
+)
+
+// These constants are the fully-qualified names of the RPCs defined in this 
package. They're
+// exposed at runtime as procedure and as the final two segments of the HTTP 
route.
+//
+// Note that these are different from the fully-qualified method names used by
+// google.golang.org/protobuf/reflect/protoreflect. To convert from these 
constants to
+// reflection-formatted method names, remove the leading slash and convert the 
remaining slash to a
+// period.
+const (
+       // GateServiceSendProcedure is the fully-qualified name of the 
GateService's Send RPC.
+       GateServiceSendProcedure = "/gate.GateService/Send"
+)
+
+var (
+       _ GateService = (*GateServiceImpl)(nil)
+)
+
+// GateService is a client for the gate.GateService service.
+type GateService interface {
+       Send(ctx context.Context, req *SendRequest, opts ...client.CallOption) 
(*Result, error)
+}
+
+// NewGateService constructs a client for the gate.GateService service.
+func NewGateService(cli *client.Client, opts ...client.ReferenceOption) 
(GateService, error) {
+       conn, err := cli.DialWithInfo("gate.GateService", 
&GateService_ClientInfo, opts...)
+       if err != nil {
+               return nil, err
+       }
+       return &GateServiceImpl{
+               conn: conn,
+       }, nil
+}
+
+func SetConsumerGateService(srv common.RPCService) {
+       dubbo.SetConsumerServiceWithInfo(srv, &GateService_ClientInfo)
+}
+
+// GateServiceImpl implements GateService.
+type GateServiceImpl struct {
+       conn *client.Connection
+}
+
+func (c *GateServiceImpl) Send(ctx context.Context, req *SendRequest, opts 
...client.CallOption) (*Result, error) {
+       resp := new(Result)
+       if err := c.conn.CallUnary(ctx, []interface{}{req}, resp, "Send", 
opts...); err != nil {
+               return nil, err
+       }
+       return resp, nil
+}
+
+var GateService_ClientInfo = client.ClientInfo{
+       InterfaceName: "gate.GateService",
+       MethodNames:   []string{"Send"},
+       ConnectionInjectFunc: func(dubboCliRaw interface{}, conn 
*client.Connection) {
+               dubboCli := dubboCliRaw.(*GateServiceImpl)
+               dubboCli.conn = conn
+       },
+}
+
+// GateServiceHandler is an implementation of the gate.GateService service.
+type GateServiceHandler interface {
+       Send(context.Context, *SendRequest) (*Result, error)
+}
+
+func RegisterGateServiceHandler(srv *server.Server, hdlr GateServiceHandler, 
opts ...server.ServiceOption) error {
+       return srv.Register(hdlr, &GateService_ServiceInfo, opts...)
+}
+
+func SetProviderGateService(srv common.RPCService) {
+       dubbo.SetProviderServiceWithInfo(srv, &GateService_ServiceInfo)
+}
+
+var GateService_ServiceInfo = server.ServiceInfo{
+       InterfaceName: "gate.GateService",
+       ServiceType:   (*GateServiceHandler)(nil),
+       Methods: []server.MethodInfo{
+               {
+                       Name: "Send",
+                       Type: constant.CallUnary,
+                       ReqInitFunc: func() interface{} {
+                               return new(SendRequest)
+                       },
+                       MethodFunc: func(ctx context.Context, args 
[]interface{}, handler interface{}) (interface{}, error) {
+                               req := args[0].(*SendRequest)
+                               res, err := 
handler.(GateServiceHandler).Send(ctx, req)
+                               if err != nil {
+                                       return nil, err
+                               }
+                               return triple_protocol.NewResponse(res), nil
+                       },
+               },
+       },
+}
diff --git a/game/website/css/style.css b/game/website/css/style.css
new file mode 100644
index 00000000..a7fb09b6
--- /dev/null
+++ b/game/website/css/style.css
@@ -0,0 +1,238 @@
+/**
+ * Game stylesheet
+ * Includes responsive design and modern styles
+ */
+
+* {
+    margin: 0;
+    padding: 0;
+    box-sizing: border-box;
+}
+
+body {
+    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 
'Oxygen',
+        'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
+        sans-serif;
+    -webkit-font-smoothing: antialiased;
+    -moz-osx-font-smoothing: grayscale;
+    overflow: hidden;
+}
+
+/* Loading progress bar */
+#myProgress {
+    width: 100%;
+    height: 30px;
+    background-color: rgba(221, 221, 221, 0.8);
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 1000;
+    border-bottom: 2px solid #4CAF50;
+}
+
+#myBar {
+    width: 0%;
+    height: 100%;
+    background: linear-gradient(90deg, #4CAF50, #45a049);
+    text-align: center;
+    line-height: 30px;
+    color: white;
+    font-weight: bold;
+    transition: width 0.1s ease;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
+}
+
+/* Background */
+.background {
+    background-image: url('../img/bac.png');
+    background-size: cover;
+    background-position: center;
+    background-repeat: no-repeat;
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    min-height: 100vh;
+}
+
+/* Gate */
+.gate {
+    width: 8rem;
+    height: 4rem;
+    top: 5%;
+    left: 50%;
+    margin-left: -4rem;
+    position: absolute;
+    z-index: 10;
+    transition: margin-left 0.02s linear;
+    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
+}
+
+/* Ball */
+.ball {
+    width: 3rem;
+    height: 3rem;
+    bottom: 0;
+    left: 50%;
+    margin-left: -1.5rem;
+    position: absolute;
+    z-index: 20;
+    transition: bottom 0.02s linear;
+    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
+    cursor: pointer;
+}
+
+/* Person */
+.people {
+    width: 5rem;
+    height: 10rem;
+    bottom: 0;
+    left: 50%;
+    margin-left: -2.5rem;
+    position: absolute;
+    z-index: 15;
+    cursor: pointer;
+    transition: transform 0.1s ease;
+    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
+}
+
+.people:hover {
+    transform: scale(1.05);
+}
+
+.people:active {
+    transform: scale(0.95);
+}
+
+/* User info display */
+.info {
+    position: absolute;
+    top: 50px;
+    left: 20px;
+    background: rgba(0, 0, 0, 0.6);
+    padding: 15px 20px;
+    border-radius: 8px;
+    color: white;
+    font-size: 16px;
+    line-height: 1.8;
+    z-index: 100;
+    backdrop-filter: blur(10px);
+    box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
+    min-width: 180px;
+}
+
+.info-item {
+    display: flex;
+    align-items: center;
+    margin-bottom: 8px;
+}
+
+.info-item:last-child {
+    margin-bottom: 0;
+}
+
+.info-label {
+    font-weight: 500;
+    margin-right: 8px;
+    color: rgba(255, 255, 255, 0.9);
+}
+
+.info-value {
+    font-weight: bold;
+    color: #4CAF50;
+    font-size: 18px;
+}
+
+/* Dialog footer style */
+.dialog-footer {
+    display: flex;
+    justify-content: flex-end;
+}
+
+/* Responsive design */
+@media (max-width: 768px) {
+    .gate {
+        width: 6rem;
+        height: 3rem;
+        margin-left: -3rem;
+    }
+
+    .ball {
+        width: 2.5rem;
+        height: 2.5rem;
+        margin-left: -1.25rem;
+    }
+
+    .people {
+        width: 4rem;
+        height: 8rem;
+        margin-left: -2rem;
+    }
+
+    .info {
+        top: 40px;
+        left: 10px;
+        padding: 12px 15px;
+        font-size: 14px;
+        min-width: 150px;
+    }
+
+    .info-value {
+        font-size: 16px;
+    }
+
+    .el-dialog {
+        width: 85% !important;
+    }
+}
+
+@media (max-width: 480px) {
+    .gate {
+        width: 5rem;
+        height: 2.5rem;
+        margin-left: -2.5rem;
+    }
+
+    .ball {
+        width: 2rem;
+        height: 2rem;
+        margin-left: -1rem;
+    }
+
+    .people {
+        width: 3.5rem;
+        height: 7rem;
+        margin-left: -1.75rem;
+    }
+
+    .info {
+        top: 35px;
+        left: 8px;
+        padding: 10px 12px;
+        font-size: 12px;
+        min-width: 120px;
+    }
+
+    .info-value {
+        font-size: 14px;
+    }
+}
+
+/* Animation optimization */
+@keyframes fadeIn {
+    from {
+        opacity: 0;
+    }
+    to {
+        opacity: 1;
+    }
+}
+
+.background {
+    animation: fadeIn 0.5s ease-in;
+}
+
+/* Ensure SVG images display correctly */
+img[src$=".svg"] {
+    display: block;
+    object-fit: contain;
+}
diff --git a/compatibility/game/website/img/bac.png b/game/website/img/bac.png
similarity index 100%
rename from compatibility/game/website/img/bac.png
rename to game/website/img/bac.png
diff --git a/game/website/img/ball.svg b/game/website/img/ball.svg
new file mode 100644
index 00000000..ab005139
--- /dev/null
+++ b/game/website/img/ball.svg
@@ -0,0 +1,11 @@
+<svg id="ball" class="ball" viewBox="0 0 1365 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; width="400" height="400">
+  <path d="M647.937 1023.573c-40.96 
0-82.133-5.12-122.24-14.933-66.773-16.427-128.427-45.44-183.04-86.4-52.693-39.467-96.853-88.107-131.2-144.853-34.347-56.747-56.96-118.4-67.2-183.467-10.88-67.413-7.893-135.467
 8.533-202.24 14.08-56.747 37.334-110.293 69.12-158.72 30.934-46.72 
69.12-88.107 113.707-122.453 44.16-34.134 93.227-60.587 145.92-78.934 
53.547-18.56 109.44-27.946 165.973-27.946 40.96 0 82.134 5.12 122.24 14.933 
66.774 16.427 128.427 45.44 183.04 86.4 52.694 39.467 96.854 88.107 [...]
+  <path d="M749.27 37.547l21.974 5.333L615.51 679.04l-21.973-5.333L749.27 
37.547zM628.95 522.24L281.43 845.867" fill="#333333" p-id="1803"></path>
+  <path d="M621.27 513.92l15.36 16.64-347.306 323.627-15.36-16.64L621.27 
513.92z m521.814 18.133l-662.187-35.626" fill="#333333" p-id="1804"></path>
+  <path d="M481.537 485.12l662.187 35.627-1.28 22.613-662.187-35.627 
1.28-22.613z m314.667 496.64L603.99 374.613" fill="#333333" p-id="1805"></path>
+  <path d="M614.87 371.2l192.214 607.147-21.547 6.826-192.213-607.146 
21.546-6.827z m-397.44-63.147l581.334 268.16" fill="#333333" p-id="1806"></path>
+  <path d="M222.124 297.813l581.546 268.16-9.386 20.48-581.76-267.946 
9.6-20.694z m0 0" fill="#333333" p-id="1807"></path>
+  <path d="M364.844 376.747L336.47 135.253l-11.306-2.346C247.51 198.4 189.057 
288.213 162.817 394.453c-6.613 26.667-10.88 53.76-13.013 81.28l215.04-98.986z 
m47.573 351.573L178.39 684.16c45.44 124.16 139.52 228.693 264.96 
284.587L412.417 728.32z m177.707 273.92l4.693 7.04c136.32 14.507 268.587-28.373 
369.493-111.573l-221.013-84.694-153.173 189.227z m376.96-472.107l125.866 
205.654c16-32.214 28.587-66.56 37.547-102.827 23.68-96.213 
17.707-192.64-12.16-279.893L967.084 530.133zM562.39 22.4l15 [...]
+</svg>
+
+
diff --git a/game/website/img/gate.svg b/game/website/img/gate.svg
new file mode 100644
index 00000000..a5fc854f
--- /dev/null
+++ b/game/website/img/gate.svg
@@ -0,0 +1,5 @@
+<svg id="gate" class="gate" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; width="400" height="400">
+  <path d="M861.414041 192.806237 179.968861 192.806237c-51.828412 0-93.985558 
42.164309-93.985558 93.985558l0 514.915399 61.975517 0 0-0.276293 0.796132 
0.650823 74.704422-89.905636 597.571934 0 72.39482 87.839581 0 1.691525 
62.007239 0L955.433368 286.791795C955.432345 234.970546 913.228127 192.806237 
861.414041 192.806237zM199.153806 689.236634l-51.196009 61.657268L147.957797 
286.791795c0-2.625804 0.422625-5.13802 0.992607-7.584746l50.203402 
55.610552L199.153806 689.236634zM232.481865  [...]
+</svg>
+
+
diff --git a/game/website/img/people.svg b/game/website/img/people.svg
new file mode 100644
index 00000000..36c8f802
--- /dev/null
+++ b/game/website/img/people.svg
@@ -0,0 +1,7 @@
+<svg class="people" viewBox="0 0 1024 1024" version="1.1" 
xmlns="http://www.w3.org/2000/svg"; width="400" height="400">
+  <path d="M563.240423 102.320551m-102.320551 0a102.320551 102.320551 0 1 0 
204.641102 0 102.320551 102.320551 0 1 0-204.641102 0Z" fill="#e6e6e6"></path>
+  <path d="M322.895467 827.809372c-8.907907 20.054828-155.767992 
195.805422-155.767992 195.805421h122.447605s106.292996-119.58263 
113.924906-134.027884 38.954036-122.784661 
38.954035-122.784661l-74.633813-83.469495s-36.113136 124.397715-44.924741 
144.476619z" fill="#e6e6e6"></path>
+  <path d="M837.50765 
515.815955c0-0.409282-90.571744-32.718501-102.609456-37.533586s-29.684998-14.445254-38.520678-32.911104-29.083112-75.957962-44.130252-99.503727c-9.10051-14.228575-57.781017-92.425552-157.284743-88.284579-49.860203
 2.094562-175.750594 47.019303-207.506078 65.003644-47.693415 
27.301531-124.542167 159.427456-124.542167 159.427457A50.895446 50.895446 0 1 0 
247.033807 539.289493s68.927938-109.90431 
85.877036-125.192204c19.934451-17.815814 81.302706-35.824231 92.955211-37 [...]
+</svg>
+
+
diff --git a/game/website/index.html b/game/website/index.html
new file mode 100644
index 00000000..d16aaae1
--- /dev/null
+++ b/game/website/index.html
@@ -0,0 +1,76 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+<head>
+    <meta charset="UTF-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>FootBall Game</title>
+    
+    <script src="https://unpkg.com/vue@3/dist/vue.global.js";></script>
+    
+    <link rel="stylesheet" 
href="https://unpkg.com/element-plus/dist/index.css";>
+    
+    <link rel="stylesheet" href="css/style.css">
+    
+    <script src="https://unpkg.com/element-plus/dist/index.full.js";></script>
+</head>
+
+<body>
+    <div id="app" class="background">
+        <div id="myProgress" v-if="loadingProgress < 100">
+            <div id="myBar">{{ loadingProgress }}%</div>
+        </div>
+        
+        <div class="info" v-if="!dialogVisible">
+            <div class="info-item">
+                <span class="info-label">用户:</span>
+                <span class="info-value">{{ info.name }}</span>
+            </div>
+            <div class="info-item">
+                <span class="info-label">分数:</span>
+                <span class="info-value">{{ info.score }}</span>
+            </div>
+            <div class="info-item">
+                <span class="info-label">排名:</span>
+                <span class="info-value">{{ info.rank }}</span>
+            </div>
+        </div>
+        
+        <img id="gate" src="img/gate.svg" alt="球门" class="gate" />
+        
+        <img id="ball" src="img/ball.svg" alt="足球" class="ball" />
+        
+        <img class="people" src="img/people.svg" alt="球员" @click="clickPeople" 
/>
+        
+        <el-dialog
+            v-model="dialogVisible"
+            title="输入姓名"
+            width="30%"
+            :show-close="false"
+            :close-on-click-modal="false"
+            :close-on-press-escape="false"
+        >
+            <el-form :model="info">
+                <el-form-item label="姓名">
+                    <el-input 
+                        v-model="info.name" 
+                        placeholder="请输入您的姓名"
+                        @keyup.enter="submitInfo"
+                        autofocus
+                    ></el-input>
+                </el-form-item>
+            </el-form>
+            <template #footer>
+                <span class="dialog-footer">
+                    <el-button type="primary" @click="submitInfo">确 
定</el-button>
+                </span>
+            </template>
+        </el-dialog>
+    </div>
+    
+    <script src="js/api.js"></script>
+    <script src="js/index.js"></script>
+</body>
+
+</html>
diff --git a/game/website/js/api.js b/game/website/js/api.js
new file mode 100644
index 00000000..21ce7d35
--- /dev/null
+++ b/game/website/js/api.js
@@ -0,0 +1,77 @@
+/**
+ * API wrapper
+ * Using fetch API instead of jQuery
+ */
+
+const baseURL = 'http://127.0.0.1:8089/';
+
+/**
+ * Unified API request handler
+ * @param {string} url - Request URL
+ * @param {object} options - Fetch options
+ * @returns {Promise} Returns Promise
+ */
+async function request(url, options = {}) {
+    try {
+        const response = await fetch(url, {
+            ...options,
+            headers: {
+                'Content-Type': 'application/json',
+                ...options.headers,
+            },
+        });
+
+        if (!response.ok) {
+            throw new Error(`HTTP error! status: ${response.status}`);
+        }
+
+        const data = await response.json();
+        
+        if (data.code !== 0) {
+            throw new Error(data.msg || 'Request failed');
+        }
+
+        return data;
+    } catch (error) {
+        throw error;
+    }
+}
+
+/**
+ * User login
+ * @param {object} data - Login data {name: string}
+ * @returns {Promise} Returns Promise
+ */
+function login(data) {
+    const params = new URLSearchParams({ name: data.name });
+    return request(`${baseURL}login?${params.toString()}`, {
+        method: 'GET',
+    });
+}
+
+/**
+ * Submit score
+ * @param {object} data - Score data {name: string, score: number}
+ * @returns {Promise} Returns Promise
+ */
+function score(data) {
+    return request(`${baseURL}score`, {
+        method: 'POST',
+        body: JSON.stringify({
+            name: data.name,
+            score: data.score,
+        }),
+    });
+}
+
+/**
+ * Get rank
+ * @param {object} data - Rank query data {name: string}
+ * @returns {Promise} Returns Promise
+ */
+function rank(data) {
+    const params = new URLSearchParams({ name: data.name });
+    return request(`${baseURL}rank?${params.toString()}`, {
+        method: 'GET',
+    });
+}
diff --git a/game/website/js/index.js b/game/website/js/index.js
new file mode 100644
index 00000000..d597b19e
--- /dev/null
+++ b/game/website/js/index.js
@@ -0,0 +1,228 @@
+/**
+ * Game main logic
+ * Using Vue 3 Composition API
+ */
+
+const { createApp, ref, reactive, onMounted, onUnmounted } = Vue;
+const { ElMessage } = ElementPlus;
+
+createApp({
+    setup() {
+        // Reactive data
+        const dialogVisible = ref(true);
+        const info = reactive({
+            name: '',
+            score: 0,
+            rank: 0,
+        });
+        const isMeet = ref(false);
+        const loadingProgress = ref(0);
+
+        // Animation interval IDs storage for cleanup
+        const intervals = ref({
+            progress: null,
+            gate: null,
+            ball: null,
+        });
+
+        /**
+         * Get rank
+         */
+        const getRank = async () => {
+            try {
+                const response = await rank({ name: info.name });
+                if (response.data && response.data.rank !== undefined) {
+                    info.rank = response.data.rank;
+                }
+            } catch (error) {
+                ElMessage({
+                    message: `Failed to get rank: ${error.message || error}`,
+                    type: 'error',
+                });
+            }
+        };
+
+        /**
+         * Submit user info
+         */
+        const submitInfo = async () => {
+            if (!info.name.trim()) {
+                ElMessage({
+                    message: 'Please enter your name',
+                    type: 'warning',
+                });
+                return;
+            }
+
+            dialogVisible.value = false;
+            try {
+                const response = await login({ name: info.name });
+                if (response.data && response.data.to) {
+                    info.name = response.data.to;
+                }
+                if (response.data && response.data.score !== undefined) {
+                    info.score = response.data.score;
+                }
+                ElMessage({
+                    message: `Welcome ${response.msg || info.name}`,
+                    type: 'success',
+                });
+                await getRank();
+            } catch (error) {
+                ElMessage({
+                    message: `Login failed: ${error.message || error}`,
+                    type: 'error',
+                });
+                dialogVisible.value = true;
+            }
+        };
+
+        /**
+         * Move ball (kick animation)
+         */
+        const moveBall = () => {
+            const ballElement = document.querySelector('.ball');
+            const gateElement = document.getElementById('gate');
+            
+            if (!ballElement || !gateElement) {
+                return;
+            }
+
+            // Clear previous animation
+            if (intervals.value.ball) {
+                clearInterval(intervals.value.ball);
+            }
+
+            let tempHeight = 0;
+            const clientHeight = document.documentElement.clientHeight;
+
+            intervals.value.ball = setInterval(() => {
+                const ballRect = ballElement.getBoundingClientRect();
+                const gateRect = gateElement.getBoundingClientRect();
+                
+                const space = ballRect.left - gateRect.left;
+                const height = ballRect.top - gateRect.top;
+
+                // Detect goal (fixed collision detection logic)
+                if (!isMeet.value && space < 60 && space > -20 && height < 20 
&& height > -20) {
+                    isMeet.value = true;
+                    clearInterval(intervals.value.ball);
+                    intervals.value.ball = null;
+
+                    // Submit score
+                    score({ name: info.name, score: 1 })
+                        .then((response) => {
+                            if (response.data && response.data.score !== 
undefined) {
+                                info.score = response.data.score;
+                            }
+                            ElMessage({
+                                message: `Goal! Total score: 
${response.data.score || info.score}`,
+                                type: 'success',
+                            });
+                            getRank();
+                        })
+                        .catch((error) => {
+                            ElMessage({
+                                message: `Failed to record score: 
${error.message || error}`,
+                                type: 'error',
+                            });
+                        });
+                }
+
+                // Ball moves to bottom
+                if (tempHeight >= clientHeight) {
+                    ballElement.style.bottom = '0px';
+                    clearInterval(intervals.value.ball);
+                    intervals.value.ball = null;
+                } else {
+                    tempHeight += 20;
+                    ballElement.style.bottom = `${tempHeight}px`;
+                }
+            }, 20);
+        };
+
+        /**
+         * Loading progress bar animation
+         */
+        const moveProgress = () => {
+            const progressBar = document.getElementById('myBar');
+            if (!progressBar) {
+                return;
+            }
+
+            let width = 0;
+            intervals.value.progress = setInterval(() => {
+                if (width >= 100) {
+                    clearInterval(intervals.value.progress);
+                    intervals.value.progress = null;
+                    progressBar.remove();
+                } else {
+                    width++;
+                    loadingProgress.value = width;
+                    progressBar.style.width = `${width}%`;
+                    progressBar.textContent = `${width}%`;
+                }
+            }, 20);
+        };
+
+        /**
+         * Move gate (left-right animation)
+         */
+        const moveGate = () => {
+            const gateElement = document.getElementById('gate');
+            if (!gateElement) {
+                return;
+            }
+
+            // Get initial marginLeft (set to -4rem in CSS, which is -64px)
+            const initialMargin = -64; // 4rem = 64px
+            let tempValue = 0;
+            let increase = 1;
+
+            intervals.value.gate = setInterval(() => {
+                if (tempValue > 100 || tempValue < -100) {
+                    increase = -increase;
+                }
+                tempValue += increase;
+                // Move based on initial position
+                gateElement.style.marginLeft = `${initialMargin + 
tempValue}px`;
+            }, 20);
+        };
+
+        /**
+         * Click person to kick ball
+         */
+        const clickPeople = () => {
+            isMeet.value = false;
+            moveBall();
+        };
+
+        // Start animations when component is mounted
+        onMounted(() => {
+            moveProgress();
+            moveGate();
+        });
+
+        // Clean up all animations when component is unmounted
+        onUnmounted(() => {
+            if (intervals.value.progress) {
+                clearInterval(intervals.value.progress);
+            }
+            if (intervals.value.gate) {
+                clearInterval(intervals.value.gate);
+            }
+            if (intervals.value.ball) {
+                clearInterval(intervals.value.ball);
+            }
+        });
+
+        return {
+            dialogVisible,
+            info,
+            isMeet,
+            loadingProgress,
+            submitInfo,
+            clickPeople,
+        };
+    },
+}).use(ElementPlus).mount('#app');
diff --git a/integrate_test/game/game/tests/integration/main_test.go 
b/integrate_test/game/game/tests/integration/main_test.go
new file mode 100644
index 00000000..3588bcba
--- /dev/null
+++ b/integrate_test/game/game/tests/integration/main_test.go
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package integration
+
+import (
+       "fmt"
+       "net"
+       "os"
+       "os/exec"
+       "path/filepath"
+       "testing"
+       "time"
+)
+
+import (
+       "dubbo.apache.org/dubbo-go/v3/client"
+       _ "dubbo.apache.org/dubbo-go/v3/imports"
+)
+
+import (
+       game "github.com/apache/dubbo-go-samples/game/proto/game"
+)
+
+var gameService game.GameService
+
+func TestMain(m *testing.M) {
+       stopGate, err := ensureGateServer()
+       if err != nil {
+               panic(err)
+       }
+
+       cli, err := client.NewClient(
+               client.WithClientURL(envOrDefault("GAME_SERVER_ADDR", 
"127.0.0.1:20000")),
+       )
+       if err != nil {
+               panic(err)
+       }
+
+       gameService, err = game.NewGameService(cli)
+       if err != nil {
+               panic(err)
+       }
+
+       code := m.Run()
+
+       if stopGate != nil {
+               stopGate()
+       }
+
+       os.Exit(code)
+}
+
+func envOrDefault(key, fallback string) string {
+       if val := os.Getenv(key); val != "" {
+               return val
+       }
+       return fallback
+}
+
+func ensureGateServer() (func(), error) {
+       addr := envOrDefault("GATE_SERVER_ADDR", "127.0.0.1:20001")
+
+       if portReachable(addr, 500*time.Millisecond) {
+               return nil, nil
+       }
+
+       root, err := repoRoot()
+       if err != nil {
+               return nil, err
+       }
+
+       cmd := exec.Command("go", "run", "./game/gate/go-server/cmd")
+       cmd.Dir = root
+       cmd.Stdout = os.Stdout
+       cmd.Stderr = os.Stderr
+
+       if err := cmd.Start(); err != nil {
+               return nil, fmt.Errorf("failed to start gate server: %w", err)
+       }
+
+       if err := waitForPort(addr, 15*time.Second); err != nil {
+               _ = cmd.Process.Kill()
+               return nil, fmt.Errorf("gate server failed to start: %w", err)
+       }
+
+       return func() {
+               _ = cmd.Process.Signal(os.Interrupt)
+               done := make(chan struct{})
+               go func() {
+                       _ = cmd.Wait()
+                       close(done)
+               }()
+
+               select {
+               case <-done:
+               case <-time.After(5 * time.Second):
+                       _ = cmd.Process.Kill()
+               }
+       }, nil
+}
+
+func waitForPort(addr string, timeout time.Duration) error {
+       deadline := time.Now().Add(timeout)
+       for time.Now().Before(deadline) {
+               if portReachable(addr, 500*time.Millisecond) {
+                       return nil
+               }
+               time.Sleep(200 * time.Millisecond)
+       }
+       return fmt.Errorf("timed out waiting for %s", addr)
+}
+
+func portReachable(addr string, timeout time.Duration) bool {
+       conn, err := net.DialTimeout("tcp", addr, timeout)
+       if err != nil {
+               return false
+       }
+       _ = conn.Close()
+       return true
+}
+
+func repoRoot() (string, error) {
+       dir, err := os.Getwd()
+       if err != nil {
+               return "", err
+       }
+       for {
+               if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
+                       return dir, nil
+               }
+               parent := filepath.Dir(dir)
+               if parent == dir {
+                       return "", fmt.Errorf("go.mod not found from %s", dir)
+               }
+               dir = parent
+       }
+}
diff --git 
a/integrate_test/compatibility/game/go-server-game/tests/integration/provider_test.go
 b/integrate_test/game/game/tests/integration/provider_test.go
similarity index 55%
rename from 
integrate_test/compatibility/game/go-server-game/tests/integration/provider_test.go
rename to integrate_test/game/game/tests/integration/provider_test.go
index 2d1774a7..9cb664e2 100644
--- 
a/integrate_test/compatibility/game/go-server-game/tests/integration/provider_test.go
+++ b/integrate_test/game/game/tests/integration/provider_test.go
@@ -26,26 +26,53 @@ import (
        "github.com/stretchr/testify/assert"
 )
 
+import (
+       game "github.com/apache/dubbo-go-samples/game/proto/game"
+)
+
 func TestLogin(t *testing.T) {
-       res, err := gameProvider.Login(context.TODO(), "A001")
+       uid := t.Name()
+       res := login(t, uid)
+       data := res.Data.AsMap()
+       assert.Equal(t, uid, data["to"])
+       assert.EqualValues(t, 0, data["score"])
+}
+
+func TestScore(t *testing.T) {
+       uid := t.Name()
+       _ = login(t, uid)
+
+       res, err := gameService.Score(context.TODO(), &game.ScoreRequest{Uid: 
uid, Score: "1"})
        assert.Nil(t, err)
        assert.NotNil(t, res)
        assert.Equal(t, int32(0), res.Code)
-       assert.NotNil(t, res.Data)
-       assert.Equal(t, "A001", res.Data["to"])
-       assert.Equal(t, 0, res.Data["score"])
+       data := res.Data.AsMap()
+       assert.Equal(t, uid, data["to"])
+       assert.EqualValues(t, 1, data["score"])
 }
 
-func TestScore(t *testing.T) {
-       res, err := gameProvider.Score(context.TODO(), "A001", "1")
+func TestRank(t *testing.T) {
+       uid := t.Name()
+       _ = login(t, uid)
+
+       // boost score so this user ranks first regardless of previous tests
+       _, err := gameService.Score(context.TODO(), &game.ScoreRequest{Uid: 
uid, Score: "100"})
+       assert.Nil(t, err)
+
+       res, err := gameService.Rank(context.TODO(), &game.RankRequest{Uid: 
uid})
        assert.Nil(t, err)
        assert.NotNil(t, res)
        assert.Equal(t, int32(0), res.Code)
+       data := res.Data.AsMap()
+       assert.Equal(t, uid, data["to"])
+       assert.EqualValues(t, 1, data["rank"])
 }
 
-func TestRank(t *testing.T) {
-       res, err := gameProvider.Rank(context.TODO(), "A001")
+func login(t *testing.T, uid string) *game.Result {
+       t.Helper()
+       res, err := gameService.Login(context.TODO(), &game.LoginRequest{Uid: 
uid})
        assert.Nil(t, err)
        assert.NotNil(t, res)
        assert.Equal(t, int32(0), res.Code)
+       return res
 }
diff --git 
a/integrate_test/compatibility/game/go-server-game/tests/integration/main_test.go
 b/integrate_test/game/gate/tests/integration/main_test.go
similarity index 68%
rename from 
integrate_test/compatibility/game/go-server-game/tests/integration/main_test.go
rename to integrate_test/game/gate/tests/integration/main_test.go
index 14c98300..cd935c9c 100644
--- 
a/integrate_test/compatibility/game/go-server-game/tests/integration/main_test.go
+++ b/integrate_test/game/gate/tests/integration/main_test.go
@@ -23,27 +23,36 @@ import (
 )
 
 import (
-       "dubbo.apache.org/dubbo-go/v3/config"
+       "dubbo.apache.org/dubbo-go/v3/client"
        _ "dubbo.apache.org/dubbo-go/v3/imports"
-
-       hessian "github.com/apache/dubbo-go-hessian2"
 )
 
 import (
-       
"github.com/apache/dubbo-go-samples/compatibility/game/go-server-game/pkg"
-       "github.com/apache/dubbo-go-samples/compatibility/game/pkg/pojo"
+       gate "github.com/apache/dubbo-go-samples/game/proto/gate"
 )
 
-var gameProvider = pkg.BasketballService{}
+var gateService gate.GateService
 
 func TestMain(m *testing.M) {
-       config.SetConsumerService(gameProvider)
+       var err error
+       cli, err := client.NewClient(
+               client.WithClientURL(envOrDefault("GATE_SERVER_ADDR", 
"127.0.0.1:20001")),
+       )
+       if err != nil {
+               panic(err)
+       }
 
-       hessian.RegisterPOJO(&pojo.Result{})
-       err := config.Load()
+       gateService, err = gate.NewGateService(cli)
        if err != nil {
                panic(err)
        }
 
        os.Exit(m.Run())
 }
+
+func envOrDefault(key, fallback string) string {
+       if val := os.Getenv(key); val != "" {
+               return val
+       }
+       return fallback
+}
diff --git 
a/integrate_test/compatibility/game/go-server-gate/tests/integration/provider_test.go
 b/integrate_test/game/gate/tests/integration/provider_test.go
similarity index 78%
rename from 
integrate_test/compatibility/game/go-server-gate/tests/integration/provider_test.go
rename to integrate_test/game/gate/tests/integration/provider_test.go
index 66c0f611..768f5573 100644
--- 
a/integrate_test/compatibility/game/go-server-gate/tests/integration/provider_test.go
+++ b/integrate_test/game/gate/tests/integration/provider_test.go
@@ -26,12 +26,17 @@ import (
        "github.com/stretchr/testify/assert"
 )
 
+import (
+       gate "github.com/apache/dubbo-go-samples/game/proto/gate"
+)
+
 func TestSend(t *testing.T) {
-       res, err := gateProvider.Send(context.TODO(), "A001", "hello")
+       uid := t.Name()
+       res, err := gateService.Send(context.TODO(), &gate.SendRequest{Uid: 
uid, Data: "hello"})
        assert.Nil(t, err)
        assert.NotNil(t, res)
        assert.Equal(t, int32(0), res.Code)
-       assert.NotNil(t, res.Data)
-       assert.Equal(t, "A001", res.Data["to"])
-       assert.Equal(t, "hello", res.Data["message"])
+       data := res.Data.AsMap()
+       assert.Equal(t, uid, data["to"])
+       assert.Equal(t, "hello", data["message"])
 }
diff --git a/start_integrate_test.sh b/start_integrate_test.sh
index 9ad129e3..12ba57d6 100755
--- a/start_integrate_test.sh
+++ b/start_integrate_test.sh
@@ -19,8 +19,8 @@
 array+=("helloworld")
 
 # game
-#array+=("game/go-server-game")
-#array+=("game/go-server-gate")
+#array+=("game/game")
+#array+=("game/gate")
 
 # config-api
 array+=("compatibility/config-api/rpc/triple")

Reply via email to