商城系统——Lua游戏服务器开发

商城系统——Lua游戏服务器开发

Scroll Down

开头
根据原来项目的商店和需求整合了商城系统,记述下开发的思路

需求
普通的商店功能,但为了后续更有效的添加商店,做了优化处理

数据结构
数据库结构异常简单,其实在初始化的时候加载数据

功能开发
1.数据初始化管理

function StorePlug:Init()
	for k,v in pairs(StoreConfig.Config) do 
		local store = Store.new(self.player)
		local storeId = v.type
		if not self.cache[storeId] then
			self.cache[storeId] = {}
		end
		store:Init(storeId, self.cache[storeId])
		self.storeList[storeId] = store
	end
end

self.cache 读取数据库store数据,StoreConfig配置好商店列表对应storeid

function Store:Init(storeId, InitData)
    --debug
    lua_app.log_debug("Store:Init", storeId)
    self.storeId = storeId
    self.goodsRecords = InitData.goodsRecords or {}
	self.updatetime = InitData.updatetime or 0
end

storeplug为商店管理类,store商店实体类,goodsrecords储存购买物品id和数量。

2.物品购买
这是商城系统最重要的部分,但其实并不难。

1.购买传商店storeid,物品id和数量num,获取store实体类数据

--购买物品

function StorePlug:Buy(storeId, id, num)
	-- debug
	lua_app.log_debug("StorePlug:Buy", storeId, id, num)
	if not num or num == 0 then
		num =1
	end
	local store = self.storeList[storeId]
	if not store then
		return false
	end
	if not self:CheckBuy(storeId, id, num) then
		return false
	end
	--通用商店new
	return store:Buy(id, num)
end

2.读取商店配置,验证判定并保存数据

function Store:Buy(id, num)
    --debug
    lua_app.log_debug("Store:Buy", id, num)
    local cfg = self:GoodsRecordsConfig(id)
	if not self.player.bag:PutNewItemIn(cfg.item.item, cfg.item.count * num) then
        server.sendErr(self.player, "can't put into bag")
        return false
    end
	local pricecount = cfg.price.count
	--判断是否存在折扣
	if self.storeId == StoreConfig.StoreType.LuckyShop then
		pricecount = getPrice(self.storeId,id,pricecount)
		elseif cfg.discount then
		pricecount = pricecount * cfg.discount
	end
    self.player:PayReward(
        cfg.price.type,
        cfg.price.id,
        pricecount * num,
        BaseConfig.YuanbaoRecordType.Store,
        "商店购买"
    )
	self:SetGoodsRecordsCount(id, num)
	local data = self:GetMsgData()
	self:_SendPlayerToClient(data)
    --debug
    lua_app.log_debug("Store:Buy", "success")
    return true
end
 
StoreConfig.Config = {
	{type = 1,cfg = "LuckyShopConfig",storeType = "LuckyShop",limitType = 4}, 
	{type = 2,cfg = "MallConfig",storeType = "MallStore",limitType = 2},
}

StoreConfig读取storeid对应的商店配置名称,购买验证判定其实放在Store类更好

function Store:GoodsRecordsConfig(id)
	local cfg = StoreConfig.Config[self.storeId].cfg
    return server.configCenter[cfg][id]
end
--验证购买条件
function StorePlug:CheckBuy(storeId,id, count)
	local store = self.storeList[storeId]
    local cfg = store:GoodsRecordsConfig(id)
	 if not cfg then
        server.sendErr(self.player, "error cfgid")
		return false
    end
 
    -- 如果限购,检查数量 默认为1
    if cfg.limit then
		local limitcount = 1
		if cfg.num then
			limitcount = tonumber(cfg.num)
		end	
		if store:GetGoodsRecordsCount(id) + count > limitcount then
			server.sendErr(self.player, "overflow limitcount")
			return false
		end
    end
	
	-- 检查等级
	if cfg.level then
		if self.player.cache.level < cfg.level then
			server.sendErr(self.player, "not enough level")
		return false
		end
    end
	-- 检查时间
	if cfg.time then
		if lua_app.now() < getTimeStamp(cfg.time[1]) or lua_app.now() > getTimeStamp(cfg.time[2]) then
			server.sendErr(self.player, "error time")
		return false
		end
		
    end
    -- 检查钱
    if not self.player:CheckReward(cfg.price.type, cfg.price.id, cfg.price.value) then
        server.sendErr(self.player, "not enough money")
        return false
    end
    -- 检查背包
    if self.player.bag:GetBagEmptyCount() <= 0 then
        server.sendErr(self.player, "bag full")
        return false
    end
    return true
end

3.物品管理功能
根据条件分为每日每周和后续添加的12小时重置购买次数,重置类型依然配置的StoreConfig中

-- TODO:HJ player实现接口后调用这里
function StorePlug:onDayTimer()
	for _, store in pairs(self.storeList) do
		store:onDayReset()
	end
end
 
function Store:onDayReset()
    self:_ResetgoodsRecords(StoreConfig.LimitType.DayLimit)
end
 
function Store:onHalfHour()
	if StoreConfig.Config[self.storeId].limitType == StoreConfig.LimitType.HalfDayLimit then
		if lua_util.isfresh(self.updatetime) then
			self:_ResetgoodsRecords(StoreConfig.LimitType.HalfDayLimit)
		end
	end
end
 
function Store:onWeekReset()
    self:_ResetgoodsRecords(StoreConfig.LimitType.WeekLimit)
end
 
function Store:_ResetgoodsRecords(limitType)
	--商店购买数据刷新
	for goodsid, data in pairs(self.goodsRecords) do
		local cfg = self:GoodsRecordsConfig(goodsid)
		if cfg.limit.type == limitType then
			data.count = 0
		end
	end
	self.uptime = lua_app.now()
	local data = self:GetMsgData()
	self:_SendPlayerToClient(data)
end

4.推送数据给客户端
1.初始化推送全部商城数据

function StorePlug:onInitClient()
	--初始化商店信息
	for k,store in pairs(self.storeList) do 
		store:onInitClient()
	end
	--初始化玩家购买信息
	local datas = {}
	for _, store in pairs(self.storeList) do
		local data = store:GetMsgData()
		table.insert(datas, data)
	end
	server.sendReq(
		self.player,
		"sc_store_play_update",
		{
			storedatas = datas
		}
	)
end

2.购买推送商店数据

function Store:_SendPlayerToClient(data)
   --玩家购买物品信息
    local datas = {}
	table.insert(datas, data)
	server.sendReq(
		self.player,
		"sc_store_play_update",
		{
			storedatas = datas
		}
	)
end

3.协议数据结构
#store
.store_goods_data {
id 0 : integer
buyCount 1 : integer
}

.store_buy_data {
storeId 0 : integer
goods 1 : *store_goods_data
}
5.特殊商店处理
这其实不算商城系统的功能,不过有些商店可能有各种奇怪的要求,下面这个辛运商店实例:
每12小时更新一次商品,玩家数据重置已经做了,不过商店是随机生成配置的部分物品和折扣价

--刷新商店
function LuckyShop:RefreshGoods()
	local cfg = StoreConfig.Config[self.storeId].cfg
	local luckyShopConfig = server.configCenter[cfg]
	self.data = {}
	--获取随机id和折扣
	luckyShopConfig = lua_util.randArray(luckyShopConfig,6)
	for id, scfg in pairs(luckyShopConfig) do
		self.data[scfg.id] = {ratio = getRatio(scfg.discount)}	
	end
	self.updatetime = lua_app.now()
	server.settingCenter:SetData(SettingConfig.SettingType.LuckyShop,self.data)
	self:sendRecordToClient()
    return true
end

我选择的方式是创建一张全局配置表,每次刷新商店id和折扣率,初始和更新数据也发送给客户端
总体来说商店系统还是比较好做的,只要以后的商店统一配置管理,增加商店添加StoreConfig数据就行了