• Elixir游戏服设计二


    搞一个例子,而没有实际的目标,做起来真是烦人。几次三番都想放弃。

    后来想想,即使最后完成不了完整的服务器,把需要的知识点搞搞,摸熟悉也是好的。

    这里没有完整的项目目录,主要是对需要的指点进行整理。要完整写个教材的话,话费我太多时间,恐怕我继续不下去。

    先搞个建模吧。玩家数据目前如下

    defmodule Player do
        @behavior Access
        defdelegate [fetch(t, key), get_and_update(t, key, list)], to: Map
        defstruct [:base_info]
        def new(id) do
            %Player{ 
                base_info: BaseInfo.new(id)
            }
        end
    end
    defmodule BaseInfo do
        @behavior Access
        defdelegate [fetch(t, key), get_and_update(t, key, list)], to: Map
        defstruct [:id, :name, :avarta_id, :gem, :gold, :last_login, :last_logout]
        def new(id) do
            %BaseInfo{
                id: id,
                name: "",
                avarta_id: 0,
                gem: 0,
                gold: 500,
                last_login: 0,
                last_logout: 0
            }
        end
    
        def add_gem(base_info, num) when is_integer(num) and num > 0 do
            {:ok, update_in(base_info, [:gem], &(&1 + num))}
        end
    
        def cost_gem(base_info, num) when is_integer(num) and num > 0 do
            if base_info.gem >= num do
                {:ok, update_in(base_info, [:gem], &(&1 - num))}
            else 
                ErrorMsg.gem_not_enough
            end
        end
    
        def add_gold(base_info, num) when is_integer(num) and num > 0 do
            {:ok, update_in(base_info, [:gold], &(&1 + num))}
        end
    
        def cost_gold(base_info, num) when is_integer(num) and num > 0 do
            if base_info.gold >= num do
                {:ok, update_in(base_info, [:gold], &(&1 - num))}
            else 
                ErrorMsg.gold_not_enough
            end
        end
    
        def set_name(base_info, name), do: %BaseInfo{base_info| name: name}
        def set_last_login(base_info, last_login), do: %BaseInfo{base_info| last_login: last_login}
        def set_last_logout(base_info, last_logout), do: %BaseInfo{base_info| last_logout: last_logout}
        def set_avarta_id(base_info, avarta_id), do: %BaseInfo{base_info| avarta_id: avarta_id}
    
    end
    defmodule ErrorMsg do 
        for line <- File.stream!(Path.join([__DIR__, "error_msg.txt"]), [], :line) do
            if line |> String.strip |> String.starts_with?("%") do
            else
                [str_error_atom, str_error_msg] = line |> String.split(",") |> Enum.map(&String.strip(&1))
                fun_name = String.to_atom(str_error_atom)
                def unquote(fun_name)(), do: {:error, unquote(fun_name), unquote(str_error_msg)}
            end
        end
    
    
    end

    在BaseInfo模块里有用的接口,需要区分操作成功和失败,所以用{:ok, value}和 {:error, :gem_not_enough, "钻石不足"} 等来表示,一开始是

    使用模块属性如@gem_not_found {:error, :gem_not_enough}来表示的。这种对于纯erlang或者elixir已经够用了。考虑到需要客户端接入的话,

    那么还是需要中文提示之类的,又考虑到有可能错误需要在系统范围内使用。即是以前erlang服务器里定了全局的宏用法。可是elixir没有erlang宏这种东西,模块属性又是编译期用的,没法在别的模块用。所以我构建了ErrorMsg模块,动态从如下外部数据源生成函数供其他模块用。

    %% 通用提示
    gem_not_enough, 钻石不足
    gold_not_enough, 金币不足

    另外需要提到的一点,原来使用了update_in 用法,而Access Protocol在我的elixir 版本1.2.3已经废弃了,没法用@derive Access, 

    所以我搜了网上抄到上述改法(现在不知道会有什么问题)。

    最后上简单的测试代码

    defmodule BaseInfoTest do
      use ExUnit.Case
      setup do
          base_info = %BaseInfo{ gem: 10, gold: 10}
          {:ok, base_info: base_info}
      end
    
      test "add valid gem", %{base_info: base_info} do
        assert {:ok, new_base_info} = base_info |> BaseInfo.add_gem(10)
        assert new_base_info.gem == base_info.gem + 10
      end
    
      test "add invalid gem", %{base_info: base_info} do
           assert catch_error(
                       {:ok, _} = base_info |> BaseInfo.add_gem(-10) 
                   )
      end
    
      test "add float gem", %{base_info: base_info} do
          assert catch_error(
                  {:ok, _} = base_info |> BaseInfo.add_gem(10.5)
              )
      end
      
      test "cost valid gem", %{base_info: base_info} do
           assert {:ok, new_base_info} = base_info |> BaseInfo.cost_gem(10)
           assert new_base_info.gem == base_info.gem - 10
      end
    
      test "cost invalid gem", %{base_info: base_info} do
          assert catch_error base_info |> BaseInfo.cost_gem(-10)
      end
    
      test "cost float gem", %{base_info: base_info} do
          assert catch_error base_info |> BaseInfo.cost_gem(10.5)
      end
    
      test "cost gem_not_enough", %{base_info: base_info} do
          assert ErrorMsg.gem_not_enough == base_info |> BaseInfo.cost_gem(20)
      end
    
      test "add valid gold", %{base_info: base_info} do
          assert {:ok, new_base_info} = base_info |> BaseInfo.add_gold(10)
          assert new_base_info.gold == base_info.gold + 10
      end
    
      test "add invalid gold", %{base_info: base_info} do
           assert catch_error base_info |> BaseInfo.add_gold(-10)
      end
    
      test "add float gold", %{base_info: base_info} do
          assert catch_error base_info |> BaseInfo.add_gold(10.5)
      end
    
      test "cost valid gold", %{base_info: base_info} do
           assert {:ok, new_base_info} = base_info |> BaseInfo.cost_gold(10)
           assert new_base_info.gold == base_info.gold - 10
      end
    
      test "cost invalid gold", %{base_info: base_info} do
          assert catch_error base_info |> BaseInfo.cost_gold(-10)
      end
    
      test "cost float gold", %{base_info: base_info} do
          assert catch_error base_info |> BaseInfo.cost_gold(10.5)
      end
    
      test "cost gold_not_enough", %{base_info: base_info} do
          assert ErrorMsg.gold_not_enough == base_info |> BaseInfo.cost_gold(20)
      end
    
      test "set_avarta_id", %{base_info: base_info} do
            new_base_info = base_info |> BaseInfo.set_avarta_id(10001)
            assert new_base_info.avarta_id == 10001
      end
    
    end

    其他不测了,正式项目,当然还是仔细都测了好。

    留下的疑问,在ErrorMsg模块里,不知道如何把生成函数挪到for外面,我试了下没成功。知道的同学留个解决方案

    今天到此结束。明天正式建模玩家进程吧。

  • 相关阅读:
    vue-router 动态路由
    vue-router 路由配置
    vue通信之子父组件通信
    vue组件通信之父组件主动获取子组件数据和方法
    vue 笔记,ref 及 $event 事件对象
    vue组件通信之非父子组件通信
    vue组件通信之父子组件通信
    localStorage使用总结(转载)
    jQuery 实现复选框全选、反选及获取选中的值
    jQuery 对象与 Dom 对象转化
  • 原文地址:https://www.cnblogs.com/rubyist/p/5535235.html
Copyright © 2020-2023  润新知