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外面,我试了下没成功。知道的同学留个解决方案
今天到此结束。明天正式建模玩家进程吧。
Elixir游戏服设计二的更多相关文章
- Elixir游戏服设计五
在<Elixir游戏服设计一>里提到,按照系统功能划分成app要保证原子性很难, 现在想想也没那么难.保证原子性,无非就是需要某个单点去完成操作.那么选择玩家进程去做原子性工作就可以了. ...
- Elixir游戏服设计一
在Erlang游戏服设计总结http://www.cnblogs.com/rubyist/p/5530575.html里, 我提到我想要的游戏服设计方法,希望以应用做为基础构建块.最近我在学习elix ...
- 简单Elixir游戏服设计- 游戏玩法介绍
抄以前的,做了点修改. 到目前为止,我们完成了玩家的数据和进程建模,现在介绍游戏玩法. 为什么我们还不做客户端接入.协议指定呢?为什么还没有网关和数据存储呢.在我接手的游戏, 这些通常已经定下来了,我 ...
- Elixir游戏服设计三
玩家进程用gen_server来建模,我不直接使用 use GenServer, 而是使用exactor,该库可以去掉反锁的接口定义. 我们新建一个 player_server_manager app ...
- 简单Elixir游戏服设计-玩家进程跑起来
有了玩家模型,我们试试让玩家进程跑起来. 需要搞个PlayerSupervisor来负责启动和监控玩家进程. defmodule PlayerSupervisor do use Supervisor ...
- 关于Elixir游戏服设计系列
写着写着就废球了,感觉空对空,实在没什么意思. 另外很快就要搞新项目,决定新项目就直接上elixir了.目前该做的准备工作已经探索了一些了. 以下的东西是写给同事参考的,感兴趣的可以看看,提建议更好. ...
- 简单Elixir游戏服设计-玩家进程注册
上回说用Registry 做本地注册(跨服可以用syn,只是稍微麻烦点,需要模拟global注册机制,写个封装模块). 修改game_server 项目的mix.exs, 增加应用启动 def app ...
- Elixir游戏服设计四
上章说到我们要引入syn https://github.com/ostinelli/syn/ 看过文档,它并没有直接提供{via, Module, Name} 相关的方法.我们需要封装一下. Name ...
- 简单Elixir游戏服设计-玩法simple_poker
上回介绍了玩法,现在编写了玩法的简单建模. 做到现在感觉目前还没有使用umbrella的必要(也许以后会发现必要吧),model 应用完全可以合并到game_server. 代码还在https://g ...
随机推荐
- 微信小程序开发历程
小程序: 帮助文档:(https://mp.weixin.qq.com/debug/wxadoc/dev/index.html) 优势: 一种无须安装即可运行的程序,与订阅号,服务号是同一 ...
- 1st 四则运算题目生成程序
程序代码见此 程序展示 需求分析 需要程序能根据用户指定生成四则运算的题目,并且能让用户做题,并且最后打分统计正确率 功能设计 主要实现的功能就是: 接受用户输入以便知道要出多少道题目(-n x) 能 ...
- 201521123107 《Java程序设计》第1周学习总结
第1周学习总结 1.本周学习总结 本周我们正式开始了对一门新的编程语言java的学习.本周的主要内容是初步了解了java的发展过程,java具有简约且简单 .平台无关性等优点.java的3个关键的工具 ...
- 201521123031 《Java程序设计》第8周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结集合与泛型相关内容. 2. 书面作业 本次作业题集集合 1.List中指定元素的删除(题目4-1) 1.1 实验总结 答:实现con ...
- 201521123051《Java程序设计》第六周学习总结
1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰,内容覆盖 ...
- 201521123035《Java程序设计》第四周学习总结
本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 本周还讲了注释与类设计.老师用例子向我们展示实际生活中一个类里面包含了哪些属性,并由此联想到如果自 ...
- 201521123068《Java程序设计》第4周学习总结
1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 点击查看->高清脑图 1.2 使用常规方法总结其他上课内容. 答:学习继承与多态的知识,了解它们之间的关系:super.ext ...
- java第一次作业0
lsl321 java第一次作业 #1. 本章学习总结 你对于本章知识的学习总结 本章我们学习了各种java相关文件的使用,以及码云,博客,pat等程序辅助软件,这些对于我们专业的学习有非常大的帮助, ...
- Java课程设计——计算器
1.团队课程设计博客链接 http://www.cnblogs.com/yuanj/p/7072137.html 2.个人负责模块或任务说明 确定课题并进行任务分工 编写计算器删除,清零,清空,小数点 ...
- geotrellis使用(三十四)矢量瓦片技术研究——矢栅一体化
前言 本文所涉及技术与Geotrellis并无太大关系,仅是矢量瓦片前端渲染和加载技术,但是其实我这是在为Geotrellis的矢量瓦片做铺垫.很多人可能会说,Geotrellis为什么要搞矢量瓦片, ...